# -*- coding: utf-8 -*-


#远程函数调用的几种方式：
#	第一种：registermodulefunc模块函数远程调用替换，则将对应模块函数的调用直接替换成远程RPC的模块调用，
#			非常大的优势是，无须侵入式的改动原有业务逻辑，使用场合是开发就需要定好需要替换成远程调用，
#			整个开服期间无须改动回旧函数调用，当然可使用替换后的CFunctor属性localfunc可找回旧函数对象；
#	第二种：remotecall2远程模块函数调用，这种用法直接通过传入相关参数（可选nodetype/nodeidx，否则默认
#			广播调用），直接对远程的某个模块下的函数调用并且返回结果（通过传入boWait=False，无须等待
#			函数运算结果），使用场合是对于一些地方本地需要大部分情况下需要调用本地的模块函数，但是有
#			非常少的情况需要调用远程的模块函数，则可使用这种包装函数方式调用，缺点就是需要改动原有业务
#			逻辑，如：动态创建远程底层场景实例，则：remoteManage.remotecall2("engine.C_LoadScene", ...)；
#	第三种：reversecall是一种真身对象反向请求代理的成员函数的用法，该函数的调用被封装在对象基类CBaseProxyObject
#			中的成员函数ReverseCall，使用场合是必须有真身对象同时有反向请求代理的成员函数，例子如下：
#			oPlayer.ReverseCall("PlayerGoto",oPlayer.SceneId(),x1,y1)；
#	第四种：remotecall远程非框架函数调用，这种方式在使用前必须先通过register_handle_method函数，将一些
#			特殊用法并且跟业务相关的逻辑定义成临时函数注册进入到remoteManage的代理框架中，好处就是添加
#			一些特殊的框架用法，无须给remoteManage模块带来框架上的改动，但是后续这种情况应该使用不多，
#			因为业务相关的特殊调用都应该可通过第二种方式可以胜任（先保留）；

from utility import *
import pickle
import time
import types
import weakref

P2P_PROXY_CALL	=247

SUB_CALL_REMOTECALL = 1		#远程请求(serverMng版本)
SUB_CALL_CALLRETURN = 2		#远程返回
SUB_CALL_REMOTECALL2= 3		#远程请求(clientMng版本)
SUB_CALL_OTHER_READY= 4		#其他主服准备好（从、共享服就绪好）

RECV_TYPE_PACKET = 1 	#接收包类型
SEND_TYPE_PACKET = 2 	#发送包类型

UBSON_TYPE = 1 	#ubson类型
PICKLE_TYPE = 2 	#pickle类型

#不需要构造远程对象方法的，将名称填到该列表（以及逆向调用的函数也需要填到这个列表）
#	备注：Remove方法先移除列表，代理对象调用则调用真实对象的方法去清理
glUseLocalMethodExe = ["GetItem","AcntObj","GetAllItem","UID","UUID","OwnerId","PlayerGoto",\
					"boProxy","SetProxyToken","GetProxyToken","SetUID","CallLater",\
					"DebugFuncCost","DelCallLater","FindCallLater","AddItem","GuildId",\
					"mro","SetAcntObj","__weakref__","ReverseCall","SetProxyParams","GetProxyParams",\
					"LoadFromDB","SaveToDB","IsVisibleTo","Goto","RespondWorldMap","Triger",]
#不需要构造远程对象方法的，但是需要覆盖本地函数执行空逻辑pass，将名称填到该列表
glUseLocalMethodNoExec = ["Time2Save","SaveToDB",]
#不需要读取远程对象属性，将属性名称填到该列表（成员函数也算属性，如果成员函数填到这里来，
#成员函数就会被代理函数覆盖，并且可以触发远程调用，但是如果成员函数名填到glUseLocalMethodExe
#则不会被远程调用覆盖，达不到远程调用效果）
glUseLocalAttribute = ["iUID","dObjMap","token","acntObj","guildMgrObj","iDbNo",\
						"Block_Loaded","AddMedalPoints","CGuild.dMember","CGuild.dCache",]
#需要访问周期容器CycXXXX的类
glAccessCycCtnClsName = ["CPlayer","CAccount","CGuild","CSysGuild"]

#这里主要应用于将下面对象作为远程调用传参的时候，自动将对象转换成对象UID，
#接收端在根据传过去的格式，提取出uid再通过下面函数获取对应的对象，再传进去
#对应的函数调用（★★★ 主要目的：减少传who、guild对象需要改动脚本层 ★★★）
#todo: 这里还可以继续优化，在框架层整合，无须增加下面配置（通过全局唯一的uuid，可以在框架层抽象这部分）
#if not globals().has_key("gdObjectGetFuncMap"):
#	gdObjectGetFuncMap = {
#		#类名：[获取UID函数,获取对象函数]
#		#通用情况"xxxx":1，实际上使用UUID和C_FindObject两个框架函数
#		#"CVtlScene":1,
#		#"CItem":["UID","Save","item.Load",],
#	}

def hasProxyTransFunc(cls):
	global gdObjectGetFuncMap
	clsname = type(cls).__name__
	return gdObjectGetFuncMap.has_key(clsname)

def regProxyTransFunc(cls, typeid, lTransOpt=1):
	#注册已经包装的类名到gdObjectGetFuncMap容器
	clsname = cls.__name__
	gdObjectGetFuncMap[typeid]=lTransOpt
	gdObjectGetFuncMap[clsname]=lTransOpt

def hasRegProxy(obj):
	global gdObjectGetFuncMap
	return True if gdObjectGetFuncMap.has_key(obj.__class__.__name__) else False

if not globals().has_key("gdRemoteModuleMethod"):
	gdRemoteModuleMethod = {}  #modulefuncname===>oldfuncobj

#localdumps = dumps
#localloads = loads
#localdumps = ubson.dumps
#localloads = ubson.loads

def localdumps(uArgs):
	try:
		return ubson.dumps(uArgs),UBSON_TYPE
	except:
		return pickle.dumps(uArgs),PICKLE_TYPE

def localloads(sInfo):
	iType=C_UnpackInt(1)
	if iType==UBSON_TYPE:
		return ubson.loads(sInfo)
	elif iType==PICKLE_TYPE:
		return pickle.loads(sInfo)
	else:
		raise Exception(Language("异常解码类型:%0$s",iType))

def dealFunction(oldFunc,newFunc): 
	for sName in dir(newFunc):
		if not sName.startswith('func_'):#只处理这类属性'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name'			
			continue
		if sName in ("func_closure","func_globals"):#这是只读的
			continue		
		attr=getattr(newFunc,sName)
		setattr(oldFunc,sName,attr)	

#查找模块，返回模块的所有属性
def import1(sPath):
    mod=__import__(sPath)
    lPart=sPath.split('.')    
    for sPart in lPart[1:]:
        mod=getattr(mod,sPart)
    return mod

#支持模块内字段内容获取
def import2(sPath,ctnType=0):
	lPart=sPath.split('.')  
	mod = __import__(lPart[0])
	for sPart in lPart[1:]:
		if type(mod) == dict:
			try:
				sPart = eval(sPart) #xxxx.xxx.xx.(1111,),截取最后的key出来，eval转换成真实的key
			except:
				sPart = sPart #如果是字符串，上述的eval抛出异常，则直接读取原来的字符串
			#作为key的基础数据类型，则当None时使用setdefault设置并且返回容器
			if type(sPart) in (int,long,tuple,):
				ctn = {} if 0==ctnType else []
				mod = mod.setdefault(sPart, ctn)
			else:
				mod = mod.get(sPart)
		elif hasattr(mod, "boProxy") and mod.boProxy.im_self and mod.boProxy():
			lastmod = mod
			mod = getattr(mod, sPart, None)
			if None == mod:
				ctn = {} if 0==ctnType else []
				setattr(lastmod, sPart, ctn) #给代理对象属性添加一个字典容器
				mod = getattr(lastmod, sPart)
		else:
			mod = getattr(mod, sPart)
	return mod
		
# localdumps = pickle.dumps
# localloads = pickle.loads

import greenlet

def convert_to_error(kind, result, *args):
	if kind == '#ERROR':
		return result
	elif kind == '#TRACEBACK':
		assert type(result) is str
		return  RemoteError(result, *args)
	elif kind == '#UNSERIALIZABLE':
		assert type(result) is str
		return RemoteError('Unserializable message: %s\n' % result, *args)
	else:
		return ValueError('Unrecognized message type')

def paramMaxLog(typeid,methname,sargs,skwds,sret=""):
	if not sret:
		if len(sargs) >= 512 or len(skwds) >=512:
			LogToFile("proxyCallParamMax", "typeid=%s|methname=%s|send|args=%s|kwds=%s|ret=%s"%\
															(typeid,methname,sargs,skwds,sret))
	else:
		if len(sret) >= 512:
			LogToFile("proxyCallParamMax", "typeid=%s|methname=%s|recv|args=%s|kwds=%s|len=%s|ret=%s"%\
													(typeid,methname,sargs,skwds,len(sret),sret[0:128]))

class RemoteError(Exception):
	def __init__(self, sErrMsg, *args):
		super(Exception, self).__init__(sErrMsg)
		self._params = args
	def __str__(self):
		sTime = time.strftime("%Y-%m-%d %H:%M:%S",LocalTime())
		return ('\n' + '-'*75 + '\n' + ">>>>>>> %s"%sTime + '\n' + \
			">>>>>>> %s"%str(self._params) + '\n' + str(self.args[0]) + '-'*75)

def AuthenticationString(sInput):
	return "proxytest"

if not globals().has_key("boLog2File"):
	boLog2File = False
if not globals().has_key("boPrintSwitch"):
	boPrintSwitch = False

def PrintLog(sLog):
	if IsInnerServer() and boPrintSwitch: Print(sLog)

def LogToFileWrap(sFileName, sLog):
	if IsInnerServer() or boLog2File: LogToFile(sFileName, sLog)

#初始化GUID
if not globals().has_key("_gCoGUID"):
	_gCoGUID = 0

#取新GUID。非线程安全
def NewCoGUID():
	global _gCoGUID
	_gCoGUID+=1
	if _gCoGUID >= 0x7FFFFFFFFFFFFFFF:
		_gCoGUID = 1
	return _gCoGUID	

#协程封装
class Greenlet(greenlet.greenlet):
	def __init__(self, func, iUID=None):
		greenlet.greenlet.__init__(self, func)
		if None <> iUID:
			self.iUID = iUID
		else:
			self.iUID = NewCoGUID()
		self.typeid = "" 	#当前协程执行的类名
		self.methname = ""	#当前协程执行的成员函数（或者普通函数）
		self.args = ""		#当前协程执行函数的参数元组
		self.kwds = ""		#当前协程执行函数的参数字典
		self.runcost = 0 	#当前协程执行耗时（纳秒）
		self.waitcost = 0 	#当前协程等待唤醒耗时（纳秒）
		self.runtotal = 0 	#当前协程完成业务运行总时间（纳秒）
		self.waittotal = 0 	#当前协程完成业务挂起等待总时间（纳秒）
		self.starttime = 0 	#开始统计开始时刻
		self.SetStartTime()
		self.proto = 0
		self.wait = False
		self.stack = ""
		self.ret = ""

	def UID(self):
		return self.iUID

	def Remove(self):
		self.SetStatInfo("Greenlet", "Remove", "", "")

	def SetStatInfo(self, typeid, methname, args, kwds, wait=False,stack="",ret=""):
		self.typeid = typeid
		self.methname = methname
		self.args = str(args)
		self.kwds = str(kwds)
		paramMaxLog(typeid,methname,self.args,self.kwds)
		self.args = self.args[0:64]
		self.kwds = self.kwds[0:64]
		if not wait: #是否统计协程挂起等待时间
			self.runcost = time.time()*1000000-self.starttime
			self.runtotal += self.runcost
		else:
			self.waitcost = time.time()*1000000-self.starttime
			self.waittotal += self.waitcost
		self.wait = wait
		self.stack = stack
		self.ret = ret
		self.SetStartTime()

	def GetWaitFlg(self):
		return self.wait

	def GetCost(self, wait=False):
		return self.runcost if not wait else self.waitcost

	def SetStartTime(self, time_t=None):
		self.starttime = time_t*1000000 if time_t else time.time()*1000000

	def GetStatInfo(self,boRet=False):
		sTag = "RET<<<<<" if boRet else "JMP>>>>>"
		sLog = "r=%s|w=%s|%s|%s%s|args=%s|kwds=%s|ret=%s|stack=%s"%(self.runcost,self.waitcost,\
						self.typeid,sTag,self.methname,self.args,self.kwds,self.ret,self.stack)
		self.CleanStatInfo()
		return sLog

	def GetStatEndInfo(self):
		return "coid=%s|rtotal=%s|wtotal=%s|total=%s"%(self.iUID,self.runtotal,\
									self.waittotal,self.runtotal+self.waittotal)

	def CleanStatInfo(self):
		self.typeid = "" 	#当前协程执行的类名
		self.methname = ""	#当前协程执行的成员函数（或者普通函数）
		self.args = ""		#当前协程执行函数的参数元组
		self.kwds = ""		#当前协程执行函数的参数字典
		self.runcost = 0 	#当前协程执行耗时（纳秒）
		self.waitcost = 0 	#当前协程等待唤醒耗时（纳秒）
		self.stack = ""
		self.ret = ""

def callback(event, args):
	fromcoid = args[0].iUID if getattr(args[0], "iUID", None) else -1
	tocoid = args[1].iUID if getattr(args[1], "iUID", None) else -1
#	LogToFile("proxymng", "[cotrace]|event:[%s] from %s[%s] to %s[%s]"%(event, \
#								id(args[0]), fromcoid, id(args[1]), tocoid))

class CoroutineWrap(object):
	def __init__(self):
		self.dCorMng = {}
		self.dCorChkTime = {}
		self.oldcallback = greenlet.settrace(callback)
		self.statistime = GetSecond()
		self.statisMap = {}

	def StatisRecordLog(self, typeid, methname, args):
		localMap = self.statisMap
		param = str(args[0]) if len(args)>0 else "nop"
		sKey = "%s.%s.%s"%(typeid,methname,param)
		localMap[sKey] = localMap.setdefault(sKey, 0)+1
		if GetSecond()-self.statistime > 5*60:
			lLog = []
			iTotalCall = 0
			lKey = localMap.keys()
			lKey.sort(key=lambda k:localMap[k],reverse=True)
			for k in lKey:
				v = localMap[k]
				iTotalCall += v
				if v >= 50: lLog.append("%s=%d"%(k,v))
			LogToFile("proxyFreqCall", "total(cnt=%s,len(lLog)=%s) \n\t%s"%(iTotalCall,len(lLog),"\n\t".join(lLog)))
			self.statisMap = {}
			self.statistime = GetSecond()

	def AddCo(self, func):
		oNewCo = Greenlet(func)
		iCoId = oNewCo.UID()
		self.dCorMng[iCoId] = oNewCo
		return iCoId

	def DelCo(self, iCoId):
		oCo = self.GetCo(iCoId)
		if None == oCo:
			raise Exception(Language("协程对象不存在，无法执行DelCo，iCoId=%0$s,oCo=%1$s",iCoId,str(oCo)))
		if not oCo.dead:
			raise Exception(Language("协程还未Dead，无法执行DelCo，iCoId=%0$s,oCo=%1$s",iCoId,str(oCo)))
		oCo.Remove()
		del self.dCorMng[iCoId]
		if self.dCorChkTime.has_key(iCoId):
			del self.dCorChkTime[iCoId]

	#将主干根协程包装成Greenlet类对象
	def InitTrunkCo(self):
		oTrunkCo = greenlet.getcurrent()
		if not hasattr(oTrunkCo, "iUID"):
			setattr(oTrunkCo, "iUID", 0)
			setattr(oTrunkCo, "proto", -1)

	def SwitchCo(self, oTarCo, *args, **kwds):
		try:
			return oTarCo.switch(*args, **kwds)
		except:
			LogPyException()

#	def HasCo(self, iCoId):
#		return True if self.GetCo(iCoId) else False

	#★★获取出去的协程对象不能直接用 if oCo 去判断，因为这样判断是判断oCo.dead
	def GetCo(self, iCoId):
		return self.dCorMng.get(iCoId)

	def IsDead(self, iCoId):
		oCo = self.GetCo(iCoId)
		return oCo.dead

	def MainRun(self, func, *args):
		return self.Resume(self.AddCo(func), *args)

	#包装外部请求处理函数
	def WrapRunCmd(self, topfunc, *args):
		self.InitTrunkCo()
		return self.MainRun(topfunc, *args)
		
	#Yield起来，实际上切回主流程
	def Yield(self, *args, **kwds):
		oCurCo = greenlet.getcurrent()
		coid = oCurCo.iUID
		if 0 == coid:
			raise Exception, Language("当前执行OnNetPacketCall主干流程还没被根协程封装好！")
		if None == self.GetCo(coid):
			raise Exception, Language("当前执行OnNetPacketCall主干流程的协程不在管理器内！")

		timerkey = "timerproxyco%s"%coid
		if FindCallLater(timerkey): 
			DelCallLater(timerkey)
		CallLater(CFunctor(self.Resume, coid, "timeout", coid), 20, timerkey)

		return self.SwitchCo(oCurCo.parent, *args, **kwds) #切回去主流程的协程

	#Rusume恢复，实际上拿出需要执行的协程继续执行
	def Resume(self, iCoId, *args, **kwds):
		sTimerKey = "timerproxyco%s"%iCoId
		if FindCallLater(sTimerKey):
			DelCallLater(sTimerKey)

		oCo = self.GetCo(iCoId)
		oCo.parent = greenlet.getcurrent()
		result = self.SwitchCo(oCo, *args, **kwds) #切回去挂起的携程
		
		if oCo.dead: self.DelCo(iCoId)
		return result

	def CheckResume(self):
		lcoid = map(lambda oCo: getattr(oCo, "proto", 0), self.dCorMng.values())
		sLog = "active co len=%s,len2=%s,len3=%s,len4=%s,len5=%s,len6=%s,coidlist=%s"%\
			(len(lcoid),len(gdCoidStat),len(gProxyServerMng.addrid_to_obj),\
			len(gProxyServerMng.id_to_refcount),len(gProxyClientMng.uid_to_proxy),len(gdCoidCost),lcoid)
		LogToFile("proxycoid",sLog)

def P2P_proxy_call(gatewayobj):
	sub = C_UnpackInt(1)
	if sub == SUB_CALL_REMOTECALL:	#请求远程对象方法等
		coid = ProxyBaseManager.ParseProxyCoId()
		gProxyServerMng.handle_request(gatewayobj, coid)
	elif sub == SUB_CALL_REMOTECALL2:
		coid = ProxyBaseManager.ParseProxyCoId()
		gProxyClientMng.handle_request(gatewayobj, coid)
	elif sub == SUB_CALL_CALLRETURN:	#协程请求返回
		coid = ProxyBaseManager.ParseProxyCoId()
		oCurCo = gCoMng.GetCo(coid)
		if None <> oCurCo:
			gCoMng.Resume(coid, "normal", coid)
	elif sub == SUB_CALL_OTHER_READY:
		import Trans
		serverId = C_UnpackInt(8)
		Trans.MainRecvReadyNotify(serverId)
#
# Type for identifying shared objects
#
class Token(object):
	'''
	Type to uniquely indentify a shared object
	类名称|节点类型|节点编号|对象ID|对象地址ID|逻辑id（如公会id等）|uuid（根据之前的信息拼凑而成）
	'''
	__slots__ = ('typeid', 'nodetype', 'nodeidx', 'uid', 'addrid', 'logicid', 'uuid')

	def __init__(self, typeid, nodetype, nodeidx, uid, addrid, logicid):
		(self.typeid, self.nodetype, self.nodeidx, self.uid, self.addrid, self.logicid, self.uuid) =\
				(typeid, nodetype, nodeidx, uid, addrid, logicid, \
				"%s|%s|%s|%s|%s|%s"%(typeid,nodetype,nodeidx,uid,addrid,logicid))

	def guid(self): #全局唯一标示
		return self.uuid

	def __getstate__(self):
		return (self.typeid, self.nodetype, self.nodeidx, self.uid, self.addrid, self.logicid)

	def __setstate__(self, state):
		(self.typeid, self.nodetype, self.nodeidx, self.uid, self.addrid, self.logicid) = state

	def __repr__(self):
		return 'Token(typeid=%r,nodetype=%r,nodeidx=%r,uid=%r,addrid=%r,logicid=%r)' % \
			(self.typeid, self.nodetype, self.nodeidx, self.uid, self.addrid, self.logicid)

def check_params(params):
	if type(params) == tuple:
		for param in params:
			if type(param) not in [int,long,float,dict,list,str,tuple,]:
				raise Exception("不要传对象等非法类型，validatetype=%s,param=%s"%\
															(type(param),param))
	elif type(params) == dict:
		for key, value in params.items():
			if type(value) not in [int,long,float,dict,list,str,tuple,]:
				raise Exception("不要传对象等非法类型，validatetype=%s,key=%s,value=%s"\
														%(type(param),key,value))

#直接调用指定服务节点上非框架函数（需要提前调用register_handle_method将非框架函数注册到对应的ProxyManage）
#nodeidx:请求节点的索引（-1：为当前类型的所有节点）
#nodetype:请求节点的类型（-1：为所有主服类型，包括主主服、从主服、共享服）
def remotecall(methodname, args=(), kwds={}, nodeidx=-1, nodetype=-1, \
								boWait=True, typeid="", gatewayobj=None):
	import init
	if IsMainServer() and not init.GetSlaveServerInfo(): return
	global gProxyServerMng
	global gProxyClientMng

	callserver = gProxyServerMng.has_handle_method(methodname)
	callClient = gProxyClientMng.has_handle_method(methodname)
	gMng = gProxyClientMng if callserver and not callClient else gProxyServerMng
	return dispatch_managerclass(gMng, methodname, args, kwds, \
					nodeidx, nodetype, boWait, typeid, gatewayobj)

#直接调用指定服务节点上的某个模块函数（无须提前注册到框架里面）
def remotecall2(modulefunc, args=(), kwds={}, nodeidx=-1, nodetype=-1, \
								boWait=True, typeid="", gatewayobj=None):
	import init
	global gProxyClientMng
	if IsMainServer() and not init.GetSlaveServerInfo(): return
	return dispatch_managerclass(gProxyClientMng, "callremotefunc", (modulefunc,)+args, kwds,\
								nodeidx, nodetype, boWait, typeid, gatewayobj)

def BindPacket2Coid(coid):
	C_BindPacket(coid, RECV_TYPE_PACKET)
	C_BindPacket(coid, SEND_TYPE_PACKET)
def RestorePacket2Coid(coid):
	C_RestorePacket(coid, RECV_TYPE_PACKET)
	C_RestorePacket(coid, SEND_TYPE_PACKET)

#instobj：返回的是弱引用而已，使用时还需要instobj()
def _func_parse(handler):
	if isinstance(handler, CFunctor):
		instobj = handler.wr() if handler.wr else None
		funcobj = handler.func if instobj else handler
	elif type(handler)==types.MethodType and handler.im_self:
		funcobj, instobj = handler.im_func, weakref.ref(handler.im_self)
	else:
		funcobj, instobj = handler, None

	if instobj:
		funcname = funcobj.__name__ #成员函数直接传函数名称
	else:							#模块函数则需要带这模块名[类名]称过去
		if type(funcobj) == types.FunctionType:
			funcname = ".".join([funcobj.__module__, funcobj.__name__])
		elif type(funcobj) == types.MethodType: #是类成员方法，但是不带instobj进来
			funcname = ".".join([funcobj.__module__, funcobj.im_class.__name__, funcobj.__name__])
		elif type(funcobj) == CFunctor:
			if not funcobj.cls:
				funcname = ".".join([funcobj.func.__module__, funcobj.func.__name__])
			else:
				funcname = ".".join([funcobj.func.__module__, funcobj.cls.__name__, funcobj.func.__name__])
		else:
			raise Exception(Language("EventObjProxy._icalc()未知的Callable,funcobj=%s"%funcobj))
	args = handler.tArgs if isinstance(handler, CFunctor) else ()
	return funcname, instobj, args

def _func_encode(handler):
	funcname, instobj, args = _func_parse(handler)
	instobj, _ = _params_encode((instobj,))
	funcargs, _ = _params_encode(args)
	encodefunc = "FUN#%s#%s#%s"%(funcname,str(instobj),str(funcargs))
	#print "encodefunc>>>>>>>>",encodefunc
	return encodefunc

def _func_decode(handler_t):
	funcname = handler_t[1]
	instobj_t, _ = _params_decode(eval(handler_t[2]))
	instobj = instobj_t[0] #这被tuple括起来所以读取首位就是instobj
	funcargs, _ = _params_decode(eval(handler_t[3]))
	#funcobj = getattr(instobj, funcname) if instobj else import2(funcname)
	#这里发送过来的函数必定是存在，否则让起支持抛出异常吧
	funcobj = object.__getattribute__(instobj, funcname) if instobj else import2(funcname)
	#print "funcname,instobj_t,funcargs>>>",funcname,instobj_t,funcargs
	return CFunctor(funcobj, *funcargs) if funcargs else funcobj

#参数编码（主要用于who、acountobj、guildobj等大对象传输的时，进行特殊编码传输）
gdFuncType = {'function':1,'CFunctor':1,'instancemethod':1}
glBaseType = {'int':1,'long':1,'float':1,'str':1,'bool':1,'list':1,'dict':1,'tuple':1,'NoneType':1,'Token':1}
def _params_encode(args=(), kwds={}):
	global gdObjectGetFuncMap, gdFuncType
	#print "gdObjectGetFuncMap>>>>>>>",gdObjectGetFuncMap
	#print "_params_encode,args,kwds>>>>",args,kwds
	args_t, kwds_t = [], {}
	for arg in args:
		arg = arg() if type(arg) == weakref.ReferenceType else arg
		clsName = type(arg).__name__
		if gdObjectGetFuncMap.has_key(clsName):
			if type(gdObjectGetFuncMap[clsName]) == list:
				funcUID = getattr(arg, gdObjectGetFuncMap[clsName][0])
				funcParam = getattr(arg, gdObjectGetFuncMap[clsName][1])
				args_t.append("%s|%s|%s|%s|%s"%("CLS",type(arg).__module__,clsName,funcUID(),funcParam()))
			else:
				args_t.append("%s|%s|%s|%s|%s"%("CLS",type(arg).__module__,clsName,arg.UUID(),""))
		elif gdFuncType.has_key(clsName):
			args_t.append(_func_encode(arg))
		elif glBaseType.has_key(clsName):
			args_t.append(arg)
		elif clsName == "instance": #没继承object的简单类对象
			_, dict_t = _params_encode((), arg.__dict__)
			args_t.append("INS|%s|%s|%s"%(arg.__class__.__module__,arg.__class__.__name__,str(dict_t)))
		else:
			raise Exception(Language("对象类%0$s远程传输先remoteManage包装！",clsName))
	for k, arg in kwds.iteritems():
		arg = arg() if type(arg) == weakref.ReferenceType else arg
		clsName = type(arg).__name__
		if gdObjectGetFuncMap.has_key(clsName):
			if type(gdObjectGetFuncMap[clsName]) == list:
				funcUID = getattr(arg, gdObjectGetFuncMap[clsName][0])
				funcParam = getattr(arg, gdObjectGetFuncMap[clsName][1])
				kwds_t[k] = "%s|%s|%s|%s|%s"%("CLS",type(arg).__module__,clsName,funcUID(),funcParam())
			else:
				kwds_t[k] = "%s|%s|%s|%s|%s"%("CLS",type(arg).__module__,clsName,arg.UUID(),"")
		elif gdFuncType.has_key(clsName):
			kwds_t[k] = _func_encode(arg)
		elif glBaseType.has_key(clsName):
			kwds_t[k] = arg
		elif clsName == "instance": #没继承object的简单类对象
			_, dict_t = _params_encode((), arg.__dict__)
			kwds_t[k] = "INS|%s|%s|%s"%(arg.__class__.__module__,arg.__class__.__name__,str(dict_t))
		else:
			raise Exception(Language("对象类%0$s远程传输先remoteManage包装！",clsName))
	#print "_params_encode:::::tuple(args_t), kwds_t>>>>",tuple(args_t), kwds_t
	return tuple(args_t), kwds_t

#参数解码（主要用于who、acountobj、guildobj等大对象传输的时，进行特殊编码传输）
def _params_decode(args=(), kwds={}):
	global gdObjectGetFuncMap
	args_t, kwds_t = [], {}
	#print "_params_decode,args,kwds>>>>>>>>>>",args,kwds
	for arg in args:
		if str == type(arg) and arg[0:3] == "CLS":
			arg_list = arg.split("|")
			modName = arg_list[1]
			clsName = arg_list[2]
			if gdObjectGetFuncMap.has_key(clsName):
				if type(gdObjectGetFuncMap[clsName]) == list:
					funcFindObj = import2(gdObjectGetFuncMap[clsName][2])
					args_t.append(funcFindObj(int(arg_list[3]), eval(arg_list[4])))
				else:
					args_t.append(C_FindObject(int(arg_list[3])))
		elif str == type(arg) and arg[0:3] == "FUN":
			func_list = arg.split("#")
			args_t.append(_func_decode(func_list))
		elif str == type(arg) and arg[0:3] == "INS": #没继承object的简单类对象
			arg_list = arg.split("|")
			modName = arg_list[1]
			clsName = arg_list[2]
			dict_s = arg_list[3]
			_, dict_t = _params_decode((), eval(dict_s))
			inst = import2("%s.%s"%(modName,clsName))()
			inst.__dict__ = dict_t
			args_t.append(inst)
		else:
			args_t.append(arg)
	for k, arg in kwds.iteritems():
		if str == type(arg) and arg[0:3] == "CLS":
			arg_list = arg.split("|")
			modName = arg_list[1]
			clsName = arg_list[2]
			if gdObjectGetFuncMap.has_key(clsName):
				if type(gdObjectGetFuncMap[clsName]) == list:
					funcFindObj = import2(gdObjectGetFuncMap[clsName][2])
					kwds_t[k] = funcFindObj(int(arg_list[3]), eval(arg_list[4]))
				else:
					kwds_t[k] = C_FindObject(int(arg_list[3]))
		elif str == type(arg) and arg[0:3] == "FUN":
			func_list = arg.split("#")
			kwds_t[k] = _func_decode(func_list)
		elif str == type(arg) and arg[0:3] == "INS": #没继承object的简单类对象
			arg_list = arg.split("|")
			modName = arg_list[1]
			clsName = arg_list[2]
			dict_s = arg_list[3]
			_, dict_t = _params_decode((), eval(dict_s))
			inst = import2("%s.%s"%(modName,clsName))()
			inst.__dict__ = dict_t
			kwds_t[k] = inst
		else:
			kwds_t[k] = arg 
	#print "_params_decode=====tuple(args_t), kwds_t>>>>>>>>>>",tuple(args_t), kwds_t
	return tuple(args_t), kwds_t

#nodeidx:int32 大于等于0指定某个主服、-1广播给除自己所有主服（而且是非阻塞）
#nodetype:int32 （-1指定serverIdx是服务编号而非索引，0主服；1从主服；2共享服；3公共服；4战斗服）
#serverNo:int32	主服编号（在场景服或者公共服请求回来主服则需要）
def dispatch_managerclass(mng, methodname, args=(), kwds={}, nodeidx=-1, \
					nodetype=-1, boWait=True, typeid="", gatewayobj=None):
	args, kwds = _params_encode(args, kwds)
	oCo = greenlet.getcurrent()
	coid = oCo.iUID
	if 0 == coid:
		raise Exception(Language("当前调用必须必须要二级协程包装才能使用dispatch_managerclass接口(proto=%0$s)",oCo.proto))
	#缓存当前的接受或者发送包到底层，协程执行完毕后恢复
	stack = str(map(lambda sstack: sstack.strip(), format_stack()[-6:-4]))
	gCoMng.StatisRecordLog(typeid,methodname,args)
	oCo.SetStatInfo(typeid, methodname, args, kwds, stack=stack) #统计进来协程之前的耗时timecost

	sArgs, sKwds = str(args), str(kwds)
	LogToFileWrap("proxymng", "[callXX] typeid=%s,methodname=%s,args=%s,kwds=%s,nodeidx=%s,nodetype=%s,addrid=%s,uid=%s,stack=%s"\
																	%(typeid,methodname,sArgs,sKwds,nodeidx,nodetype,-1,-1,stack))
	sub = SUB_CALL_REMOTECALL if isinstance(mng, ProxyClientManager) else SUB_CALL_REMOTECALL2
	if boWait: BindPacket2Coid(coid)
	try:
		ProxyBaseManager.MakeProxyPacketHead(sub, coid, gatewayobj)
		C_PacketAppendS('-1')						#对象编号
		C_PacketAppendI(-1, 8)						#对象UID
		C_PacketAppendS(typeid)					#对象类名
		C_PacketAppendS(methodname)				#调用方法
		sDumpText,iEncode=localdumps((args, kwds,))
		C_PacketAppendS(sDumpText)#调用方法参数
		C_PacketAppendI(iEncode, 1)	#编码
		C_PacketAppendI(boWait, 1)					#告知远程被调用方是否返回数据
		import Trans; Trans.SendPacket2GatewayCommon(nodeidx, nodetype)
	except:
		if boWait: RestorePacket2Coid(coid)
		LogPyException()
		raise Exception(Language("dispatch_managerclass远程请求失败！"))

	if boWait:
		coret, lastcoid = gCoMng.Yield()
		oCo.SetStatInfo(typeid, methodname, args, kwds, wait=True) #统计协程挂起来的耗时timewait
		#入口函数已经将sub和coid解析出来，这里从解析fromnodeidx,fromnodetype开始
		if "normal" == coret:
			try:
				fromnodeidx, fromnodetype = ProxyBaseManager.ParseProxyPacketHead()
				addrid = C_UnpackStr()
				uid = C_UnpackInt(8)
				backtypeid = C_UnpackStr()
				backfuncname= C_UnpackStr()
				assert coid == lastcoid, Language("返回协程编号对应不上：%s!=%s"%(coid, lastcoid))
				assert '-1' == addrid, Language("返回远程对象地址非法：%s!=%s"%(-1, addrid))
				assert -1 == uid, Language("返回的远程对象ID非法：%s!=%s"%(-1, uid))
				assert typeid == backtypeid and methodname ==backfuncname, \
					Language("返回的远程对象方法非法, %s!=%s and %s!=%s"%(typeid,backtypeid,methodname,backfuncname))
				sParam = C_UnpackStr()
				msg = localloads(sParam)
				C_ReleasePacket(RECV_TYPE_PACKET) #先删除返回的唤醒协程包
				RestorePacket2Coid(coid) #再恢复协程切出去之前的底层包环境
			except:
				msg=""
				C_ReleasePacket(RECV_TYPE_PACKET)
				RestorePacket2Coid(coid); LogPyException()
				raise Exception(Language("dispatch_managerclass远程返回失败！"))

			
			msg, _ = _params_decode(msg)
			kind, result = msg
			sResult = str(result)
			LogToFileWrap("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,addrid=%s,uid=%s,kind=%s,result=%s"\
																%(typeid,methodname,sArgs,sKwds,addrid,uid,kind,sResult))
			paramMaxLog(typeid,methodname,sArgs,sKwds,sret=sResult)
			if kind == '#RETURN':
				return result
			raise convert_to_error(kind, result, "dispatch_managerclass", addrid, uid, typeid, methodname, sArgs, sKwds)
		elif "timeout" == coret:
			RestorePacket2Coid(coid)
			LogToFile("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,addrid=%s,uid=%s"\
																%(typeid,methodname,sArgs,sKwds,"timeout",-1,-1))
			raise Exception(Language("协程%0$s超时，结束当前业务流%1$s.%2$s,args=%3$s,kwds=%4$s！",\
														coid,typeid,methodname,sArgs,sKwds,))
		else:
			RestorePacket2Coid(coid)
			LogToFile("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,addrid=%s,uid=%s"\
																%(typeid,methodname,sArgs,sKwds,"except",-1,-1))
			raise Exception(Language("协程%0$s异常唤醒，结束当前业务流%1$s.%2$s,args=%3$s,kwds=%4$s！",\
																	coid,typeid,methodname,sArgs,sKwds))
	else:
		pass

#token远程对象所在的服务节点类型和索引
def dispatch_proxyobject(mng, token, methodname, args=(), kwds={}, boWait=True):
	args, kwds = _params_encode(args, kwds)
	oCo = greenlet.getcurrent()
	coid = oCo.iUID
	if 0 == coid: 
		raise Exception(Language("当前调用必须必须要二级协程包装才能使用dispatch_proxyobject接口(proto=%0$s)",oCo.proto))
	stack = str(map(lambda sstack: sstack.strip(), format_stack()[-6:-4]))
	gCoMng.StatisRecordLog(token.typeid,methodname,args)
	#缓存当前的接受或者发送包到底层，协程执行完毕后恢复
	oCo.SetStatInfo(token.typeid, methodname, args, kwds, stack=stack) #统计进来协程之前的耗时timecost
	
	sArgs, sKwds = str(args), str(kwds)
	LogToFileWrap("proxymng", "[callXX] typeid=%s,methodname=%s,args=%s,kwds=%s,token=%s,stack=%s"\
												%(token.typeid,methodname,sArgs,sKwds,token,stack))
	sub = SUB_CALL_REMOTECALL if isinstance(mng, ProxyClientManager) else SUB_CALL_REMOTECALL2

	if boWait: BindPacket2Coid(coid)
	try:
		ProxyBaseManager.MakeProxyPacketHead(sub, coid)
		#请求远程代理对象方法等
		C_PacketAppendS(token.addrid)					#对象编号
		C_PacketAppendI(token.uid, 8)					#对象UID
		C_PacketAppendS(token.typeid)					#对象类名
		C_PacketAppendS(methodname)					#调用方法
		sDumpText,iEncode=localdumps((args, kwds,))
		C_PacketAppendS(sDumpText)		#调用方法参数
		C_PacketAppendI(iEncode, 1)#编码	
		C_PacketAppendI(boWait, 1)						#告知远程被调用方是否返回数据
		import Trans; Trans.SendPacket2GatewayCommon(token.nodeidx, token.nodetype)
	except:
		if boWait: RestorePacket2Coid()
		LogPyException()
		raise Exception(Language("dispatch_proxyobject远程请求失败！"))


	if boWait:
		coret, lastcoid = gCoMng.Yield()
		oCo.SetStatInfo(token.typeid, methodname, args, kwds, wait=True) #统计协程挂起来的耗时timewait
		#入口函数已经将sub和coid解析出来，这里从解析fromnodeidx,fromnodetype开始	
		if "normal" == coret:
			try:
				fromnodeidx, fromnodetype = ProxyBaseManager.ParseProxyPacketHead()
				addrid = C_UnpackStr()
				uid = C_UnpackInt(8)
				backtypeid = C_UnpackStr()
				backfuncname= C_UnpackStr()
				assert coid == lastcoid, Language("返回协程编号对应不上：%s!=%s"%(coid, lastcoid))
				assert token.addrid == addrid, Language("返回远程对象地址非法：%s!=%s (%s.%s|%s.%s)"%\
									(token.addrid,addrid,token.typeid,methodname,backtypeid,backfuncname))
				assert token.uid == uid, Language("返回的远程对象ID非法：%s!=%s (%s.%s|%s.%s)"%\
									(token.uid,uid,token.typeid,methodname,backtypeid,backfuncname))
				sParam = C_UnpackStr()
				msg = localloads(sParam)
				C_ReleasePacket(RECV_TYPE_PACKET) #先删除返回的唤醒协程包
				RestorePacket2Coid(coid) #再恢复协程切出去之前的底层包环境
			except:
				msg =""
				C_ReleasePacket(RECV_TYPE_PACKET)
				RestorePacket2Coid(coid); LogPyException()
				raise Exception(Language("dispatch_proxyobject远程返回失败！"))

			
			msg, _ = _params_decode(msg)
			kind, result = msg
			sResult = str(result)
			LogToFileWrap("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,result=%s,token=%s"\
													%(token.typeid,methodname,sArgs,sKwds,kind,sResult,token))
			PrintLog("[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,result=%s"\
										%(token.typeid,methodname,sArgs,sKwds,kind,sResult))
									(oCo.GetStatInfo(True),coret,kind,sResult), oCo.GetWaitFlg())
			paramMaxLog(token.typeid,methodname,sArgs,sKwds,sret=sResult)
			if kind == '#RETURN':				
				return kind, result
			raise convert_to_error(kind, result, "dispatch_proxyobject", addrid, uid, token.typeid, methodname, sArgs, kwds)
		elif "timeout" == coret:
			RestorePacket2Coid(coid)
			LogToFile("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,token=%s"\
												%(token.typeid,methodname,sArgs,sKwds,"timeout",token))
			raise Exception(Language("协程%0$s超时，结束当前业务流%1$s.%2$s,args=%3$s,kwds=%4$s,token=%5$s！",\
														coid,token.typeid,methodname,sArgs,sKwds,token))
		else:
			RestorePacket2Coid(coid)
			LogToFile("proxymng", "[return] typeid=%s,methodname=%s,args=%s,kwds=%s,kind=%s,token=%s"\
										%(token.typeid,methodname,sArgs,sKwds,"except",token))
			raise Exception(Language("协程%0$s异常唤醒，结束当前业务流%1$s.%2$s,args=%3$s,kwds=%4$s,token=%5$s！",\
																	coid,token.typeid,methodname,sArgs,sKwds,token))
	else:
		pass
		
def all_methods(obj):
	'''
	Return a list of names of methods of `obj`
	'''
	temp = []
	for name in dir(obj):
		if name not in glUseLocalMethodExe:
			func = getattr(obj, name, None)
			if func and hasattr(func, '__call__'):
				temp.append(name)
	return temp

def public_methods(obj):
	'''
	Return a list of names of methods of `obj` which do not start with '_'
	'''
	#return [name for name in all_methods(obj) if name[0] != '_']
	lmethod = [name for name in all_methods(obj) if name[0] != '_']
	#lmethod.append("__getattribute__") #支持远程获取对象点GET属性，点操作
	#lmethod.append("__setattr__") #支持远程点SET属性操作，点操作
	return lmethod

########################################################################################

class ProxyBaseManager(object):
	
	def __init__(self, authkey=''):
		self._authkey = authkey
		self.classregistry = {}
		self.name_to_meth = {}

	#serverNo:在战斗服或者公共服需要传入，告知网关需要将信息转发给哪个主服
	@classmethod
	def MakeProxyPacketHead(self, sub, coid=-1, gatewayobj=None):
		C_PacketBegin(P2P_PROXY_CALL)
		C_PacketAppendI(sub, 1)
		C_PacketAppendI(coid, 8)
		if IsMainServer() or IsSlaveServer() or IsShareServer():
			import Trans
			obj = Trans.gGatewayObj
			C_PacketAppendI(obj.GetNodeIdx(), 8)
			C_PacketAppendI(obj.GetNodeType(), 1)
		elif IsSceneServer():
			if None <> gatewayobj:
				C_PacketAppendI(gatewayobj.GetNodeIdx(), 8)
				C_PacketAppendI(gatewayobj.GetNodeType(), 1)
			else:
				raise Exception(Language("【请求场景服】依赖对应的网关对象"))
		elif IsPublicServer():
			if None <> gatewayobj:
				C_PacketAppendI(gatewayobj.GetNodeIdx(), 8)
				C_PacketAppendI(gatewayobj.GetNodeType(), 1)
			else:
				raise Exception(Language("【请求公共服】依赖对应的网关对象"))
	
	@classmethod
	def ParseProxyCoId(self):
		return C_UnpackInt(8)

	@classmethod
	def ParseProxyPacketHead(self):
		#记录来源的节点编号以及类型
		fromnodeidx = C_UnpackInt(8)
		fromnodetype = C_UnpackInt(1)
		return fromnodeidx, fromnodetype

	#非框架成员函数添加，调用此接口添加mng的成员处理函数
	def register_handle_method(self, methname, methobj):
		self.name_to_meth[methname] = methobj
		setattr(self.__class__, methname, methobj)

	def register_handle_method_by_cache(self):
		for methname, methobj in self.name_to_meth.iteritems():
			setattr(self.__class__, methname, methobj)

	def has_handle_method(self, methname):
		return self.name_to_meth.has_key(methname)

	def modulefunccall(self, *args, **kwds):
		# try:
		#PrintLog(">>>>>[模块函数调用] args=%s,kwds=%s"%(str(args),kwds))
		boBc = kwds.get("boBc", False)
		if kwds.has_key("boBc"): del kwds["boBc"]
		modulefuncname = args[-1]
		args = args[0:len(args)-1] #去掉最后一个参数modulefuncname

		modulefunc = import2(modulefuncname)
		if not modulefunc:
			raise Exception(Language("找不到模块函数：%0$s", str(modulefuncname)))
		if type(modulefunc) == types.FunctionType or type(modulefunc) == types.MethodType:
			result = modulefunc(*args, **kwds)
		else:
			result = modulefunc #也许是一个模块里面的全局变量
		return result if not boBc else ("BC_GET_TYPE", result)
		# except:
		# 	LogPyException()

	#设置代理对象或者真实对象的容身之处
	def addobjectctn(self, typeid, dparams, obj):
		if not dparams:
			return
		modName = dparams.get("modName")
		keyName = dparams.get("keyName")
		#0：默认容器dict，1：列表容器
		ctnType = dparams["ctnType"] if dparams.has_key("ctnType") else 0 #默认创建容器为dict
		boproxy = obj.boProxy()
		try:
			modValue = import2(modName,ctnType)
		except:
			LogPyException("[import2]addobjectctn异常：boproxy=%s,typeid:%s,dparams:%s,"%\
															(boproxy,typeid,dparams))
			PrintLog("[import2]addobjectctn异常：boproxy=%s,typeid:%s,dparams:%s,"%\
															(boproxy,typeid,dparams))

		if type(modValue)==dict:
			if modValue.has_key(keyName):
				PrintLog(Language("[字典]键值已存在！（模块：%0$s，键名：%1$s，是否代理：%2$s）",\
															modName,keyName,boproxy))
			try:
				modValue[keyName] = obj
			except:
				PrintLog("【WARNING】modValue字典为None，boproxy=%s,modName=%s,keyName=%s"%\
															(boproxy,modName,keyName))
				LogToFile("proxySetError", "modValue字典为None，boproxy=%s,modName=%s,keyName=%s"%\
																(boproxy,modName,keyName))
				LogPyException()
		else:
			#被重写__getattr__的对象调用hasattr都有bug，返回都是True，所以覆盖__getattr__的类，
			#不能用直接调用python原始的hasattr，而是使用覆盖后的hasattr,详细看script/utility.py中的hasattr
			if hasattr(modValue, keyName):
				PrintLog(Language("[对象]键值已存在！（模块：%0$s，键名：%1$s，是否代理：%2$s）",\
																	modName,keyName,boproxy))
			try:
				setattr(modValue, keyName, obj)
				import jitkeeper
				if isinstance(obj,jitkeeper.CMultiKeyContainer) and \
				None == getattr(obj, "dObjMap", None):
					setattr(obj, "dObjMap", {})
			except:
				PrintLog("【WARNING】modValue对象为None，modName=%s,keyName=%s,boproxy=%s"%\
															(modName,keyName,boproxy))
				LogToFile("proxySetError", "modValue对象为None，modName=%s,keyName=%s,boproxy=%s"%\
																		(modName,keyName,boproxy))
				LogPyException()

	def delobjectctn(self, typeid, dparams):
		if not dparams:
			return
		modName = dparams.get("modName")
		keyName = dparams.get("keyName")
		# try:
		modValue = import2(modName)
		# except:
		# 	PrintLog("[import2]_delobjectctn异常：typeid:%s,dparams:%s,"%(typeid,dparams))

		if type(modValue)==dict:
			if modValue.has_key(keyName):
				del modValue[keyName]
		else:
			if hasattr(modValue, keyName):
				delattr(modValue, keyName)

	#入口函数oncmd已经解析出sub和coid，coid这里传进来作为参数使用
	def handle_request(self, gatewayobj, coid):
		fromnodeidx, fromnodetype = ProxyBaseManager.ParseProxyPacketHead()
		addrid = C_UnpackStr()				#对象全局唯一编号（address(obj),无则填-1）
		uid = C_UnpackInt(8)				#对象编号（C_FindObject(uid)，无则填-1）
		typeid = C_UnpackStr()				#对象类名称
		funcname = C_UnpackStr()			#请求方法名称
		sParam = C_UnpackStr()				#请求方法参数
		args, kwds = localloads(sParam)		#pickle或者ubson解析出来调用所依赖的参数
		boReturn = C_UnpackInt(1)			#是否需要返回结果
		typeid_bak = typeid

		args, kwds = _params_decode(args, kwds)
		func = None
		if '-1' == addrid:	########### 处理mng的类方法
			PrintLog("===>> handle_request(cls),typeid=%s,funcname=%s"%(typeid, funcname))
			LogToFileWrap("proxymng", "[handl0] typeid=%s,funcname=%s,args=%s,kwds=%s,addrid=%s,uid=%s"\
													%(typeid,funcname,str(args),str(kwds),addrid,uid))
			func = getattr(self, funcname)
			try:
				result = func(*args, **kwds)
				if type(result) == tuple or type(result) == list:
					if len(result) == 2 and result[0] == "BC_GET_TYPE":
						if result[1] is None:
							return #查找不到结果不回包
						else:
							result = result[1]
			except Exception:
				msg = ('#TRACEBACK', format_exc())
				LogPyException()
			else:
				msg = ('#RETURN', result)
		else:			########### 处理obj的对象方法
			try:
				if self.addrid_to_obj.has_key(addrid):#这里有需要debug，直接改为True
					obj, exposed, gettypeid = self.addrid_to_obj[addrid]
					#if funcname not in ["__getattribute__", "Query", "__setattr__", ]:
					PrintLog("===>> handle_request(obj),typeid=%s,funcname=%s"%(typeid, funcname))
					LogToFileWrap("proxymng", "[handl2] typeid=%s,funcname=%s,args=%s,kwds=%s,addrid=%s,uid=%s"\
														%(typeid,funcname,str(args),str(kwds),addrid,uid))
	#				if funcname not in exposed:
	#					if funcname not in ["__getattribute__", "__setattr__",]:
	#						raise AttributeError(
	#								'method %r of %r object is not in exposed=%r' %
	#								(funcname, type(obj), exposed))

					func = getattr(obj, funcname, None)
					if not func: 
						raise AttributeError(
								'method %r of %r object is not in exposed=%r' %
								(funcname, type(obj), exposed))

					try:
						PrintLog("=====>>> funcname=%s,args=%s,kwds=%s"%(funcname, str(args), str(kwds)))
						result = func(*args, **kwds)
						PrintLog("=====>>> funcname=%s,result=%s"%(funcname, result))
					except TypeError:
						if funcname == "__getattribute__":
							result = getattr(obj, *args, **kwds)
							msg = ('#RETURN', result)
						else:
							msg = ('#ERROR', "TypeError: func=%s"%func)
							LogPyException()
					except KeyError:
						if funcname == "__getitem__":
							msg = ('#RETURN', None)
						else:
							msg = ('#TRACEBACK', format_exc())
					except Exception, e:
						msg = ('#ERROR', e)
						LogPyException()
					else:
						#通过方法名称映射类对象，然后再创建一个这样的对象出来
						typeid = gettypeid and gettypeid.get(funcname, None)
						if typeid:
							clsobj = ProxyServerManager.get(typeid, None)
							if typeid and clsobj: #通过方法名称创建本地对象
								obj = clsobj(tonodeidx=fromnodeidx, tonodetype=fromnodetype)
								token = obj.GetProxyToken()
								msg = ('#PROXY', (token,))
							else:
								msg = ('#RETURN', result)
						else:
							msg = ('#RETURN', result)
				else:
					msg = ('#ERROR', "[远程对象不存在]: %s.%s[args:%s,kwds:%s](addrid:%s,uid:%s)"%\
													(typeid,funcname,str(args),str(kwds),addrid,uid))
			except AttributeError:
				if funcname is None:
					msg = ('#TRACEBACK', format_exc())
				else:
					try:
						fallback_func = self.fallback_mapping[funcname]
						result = fallback_func(self, addrid, obj, *args, **kwds)
						msg = ('#RETURN', result)
					except Exception:
						msg = ('#TRACEBACK', format_exc())

			except EOFError:
				LogToFile("proxymng", 'got EOF -- exiting thread serving %r',
						   threading.current_thread().name)

			except Exception:
				msg = ('#TRACEBACK', format_exc())
		
		if boReturn:
			ProxyBaseManager.MakeProxyPacketHead(SUB_CALL_CALLRETURN, coid)
			C_PacketAppendS(addrid)				#对象全局唯一编号（address(obj),无则填-1）
			C_PacketAppendI(uid, 8)				#对象编号（C_FindObject(uid)，无则填-1）
			C_PacketAppendS(typeid or typeid_bak) 	#类型名称
			C_PacketAppendS(funcname)				#函数名称
			try:
				msg, _ = _params_encode(msg)
				sParam_t,iEncode = localdumps(msg)
				LogToFileWrap("proxymng", "[hanret] typeid=%s,funcname=%s,addrid=%s,uid=%s,result=%s"\
															%(typeid,funcname,addrid,uid,str(msg)))
				C_PacketAppendS(sParam_t)	#对象编号（C_FindObject(uid)，无则填-1）
				C_PacketAppendI(iEncode, 1)
			except Exception, e:
				msg = ('#UNSERIALIZABLE', repr(msg))
				sParam_t,iEncode = localdumps(msg)
				C_PacketAppendS(sParam_t)	#对象编号（C_FindObject(uid)，无则填-1）
				C_PacketAppendI(iEncode, 1)
			import Trans
			PrintLog("===>> handle_request(),typeid=%s,funcname=%s,msg=%s"%(typeid, funcname, str(msg)))
			Trans.SendPacket2GatewayCommon(fromnodeidx, fromnodetype)


class ProxyClientManager(ProxyBaseManager):
	def __init__(self, authkey=""):
		ProxyBaseManager.__init__(self, authkey)
		self._serializer = None
		self.uid_to_proxy = {} #管理uid到代理对象的映射关系

	def removeProxy(self, uid):
		proxy = self.uid_to_proxy.get(uid)
		if proxy: del self.uid_to_proxy[uid]

	def addProxy(self, uid, proxy):
		if not self.uid_to_proxy.get(uid):
			self.uid_to_proxy[uid] = proxy
		else:
			proxy_ = self.uid_to_proxy[uid]
			raise Exception(Language("已经存在该代理uid:%s,proxy:%s,newproxy:%s"%(uid,proxy_,proxy)))

	def getProxy(self, uid):
		return self.uid_to_proxy.get(uid)

	def isExistProxy(self, uid):
		return True if self.uid_to_proxy.get(uid) else False

	######################### 本地处理接口 #############################
	#真实对象对代理的逆向请求处理（使用这种情况比较少）
	def reversecall(self, *args, **kwds):
		uid = args[0]
		if not self.isExistProxy(uid):
			return
		curproxy = self.getProxy(uid)
		methodname = args[1]
		if not hasattr(curproxy, methodname):
			return
		methobj = getattr(curproxy, methodname)
		ret = methobj(*args[2:], **kwds)
		return ret
	#代理object最少被两种情况索引：（这里注释Remove，代理的Remove还是调回真身的Remove，统一由真身触发销毁）
	#第一种：uid_to_proxy容器；第二种：底层引用C_CreateObject
	#第三种：对象逻辑容器引用（这种情况是根据具体情况而定，不一定有，比如说公会代理就被设置到对应的逻辑容器）
	def destroyproxy(self, token):
		uid = token.uid
		proxyobj = self.getProxy(uid)
		if not proxyobj:
			return
		#清理脚本层->容器层->底层
		self.removeProxy(uid)
		lparams = proxyobj.GetProxyParams()
		if lparams:
			for dparams in lparams:
				self.delobjectctn(token.typeid, dparams) #清理obj所在的逻辑容器
		#CBaseProxyObject.Remove(proxyobj)#这个函数里面包含了C_DelObject调用
		C_DelObject(uid)

	def createproxy(self, token, exposed, dparams, *args, **kwds):
		typeid = token.typeid
		kwds["token"] = token
		kwds["exposed"] = exposed
		#PrintLog("===>> createproxy(), typeid=%s,exposed=%s"%(typeid,exposed))
		#反向请求创建代理对象
		global gdObjectGetFuncMap
		if not self.isExistProxy(token.uid):
			if hasattr(self, typeid):
				proxy = getattr(self, typeid)(*args, **kwds)
			elif gdObjectGetFuncMap.has_key(typeid):
				proxy = self.createproxyabs(typeid, dparams["modName"],\
								None, 0, NODE_TYPE_MAIN, *args, **kwds)
			else:
				raise Exception(Language("非远程包装类或者非动态远程包装类%0$s",typeid))
		else:
			proxy = self.getProxy(token.uid) #有可能一个对象被2个容器引用，如Turn对象
		proxy.SetProxyParams(dparams)
		#临时这样写，后续重构得更加好看
		if proxy.UUID() <> 0:
			if typeid == 'CPlayer':
				C_CreatePlayerObj(proxy.UUID(), proxy) 
			elif typeid == 'CDoor' or typeid=='CNpc':
				 C_CreateNpcObj(proxy.UUID(), proxy)
			elif typeid == 'CAccount':
				#todo：由于不同从服产生的连接UID有可能一样，负数的uid分段在第三个阶段处理
				#pass #需要在调用who.SetAcntObj()才再执行绑定代理who,无须注册到底层
				C_CreateObject(proxy.UUID(), proxy)
			else:
				self.addobjectctn(typeid, dparams, proxy)
				if typeid <> "list" and typeid <> "dict": #代理的list和dict不注册到底层
					C_CreateObject(proxy.UUID(), proxy)
		else:
			raise Exception(Language("typeid:%s,还有对象uid等于0"%typeid))
		return proxy

	def createproxy2(self, token, dparams, *args, **kwds):
		typeid = token.typeid
		kwds["token"] = token
		#kwds["exposed"] = exposed
		#PrintLog("===>> createproxy2(), typeid=%s,exposed=%s"%(typeid,exposed))
		#反向请求创建代理对象
		global gdObjectGetFuncMap
		if not self.isExistProxy(token.uid):
			if hasattr(self, typeid):
				proxy = getattr(self, typeid)(*args, **kwds)
			elif gdObjectGetFuncMap.has_key(typeid):
				proxy = self.createproxyabs(typeid, dparams["modName"],\
								None, 0, NODE_TYPE_MAIN, None, *args, **kwds)
				dparams.clear()
			else:
				raise Exception(Language("非远程包装类或者非动态远程包装类%0$s,可以看下regProxyTransFunc",typeid))
		else:
			proxy = self.getProxy(token.uid) 		#有可能同一个对象被2个容器引用，如Turn对象
		if dparams: proxy.SetProxyParams(dparams)	#有可能同一个代理对象被设置到不同的容器，如Turn对象
		#临时这样写，后续重构得更加好看
		if proxy.UUID() <> 0:
			if typeid == 'CPlayer':
				C_CreatePlayerObj(proxy.UUID(), proxy)
			elif typeid == 'CDoor' or typeid=='CNpc':
				 C_CreateNpcObj(proxy.UUID(), proxy)
			elif typeid == 'CAccount':
				#pass #需要在调用who.SetAcntObj()才再执行绑定代理who,无须注册到底层
				C_CreateObject(proxy.UUID(), proxy)
			else:
				self.addobjectctn(typeid, dparams, proxy)
				if typeid <> "list" and typeid <> "dict": #代理的list和dict不注册到底层
					C_CreateObject(proxy.UUID(), proxy)
		else:
			raise Exception(Language("typeid:%s,还有对象uid等于0"%typeid))
		return proxy

	def createproxyabs(self, typeid, modName, proxytype, nodeidx, nodetype, method_to_typeid, *args, **kwds):
		if IsSceneServer():
			raise Exception(Language("战斗服上不可执行创建代理%0$s！",typeid))

		if proxytype is None: proxytype = AutoProxy
		callable = import2(modName) if type(modName)==str else modName

		LogToFileWrap("proxymng", "proxy creation of a shared %r object on nodes:(%r|%r)"%\
															(typeid, nodeidx, nodetype))
		token, exp = kwds.get("token"), kwds.get("exposed")
		#if not exp: exp = public_methods(callable) #exp函数列表本地创建不走网络传输
		method_to_typeid = method_to_typeid or getattr(proxytype, '_method_to_typeid_', None)
		if method_to_typeid is not None:
			assert type(method_to_typeid) is dict
			exp = list(exp) + list(method_to_typeid)
		boremote = True if not token else False #False则仅创建本地代理对象而已
		if boremote: #被动请求远程创建真实对象
			#token, exp = self._createobject(typeid, nodeidx, nodetype, *args, **kwds)
			token = self._createobject2(typeid, nodeidx, nodetype, *args, **kwds)

		proxy = proxytype(token, self._serializer, callable, manager=self, \
										authkey=self._authkey, exposed=exp)
		return proxy

	######################### 远程处理接口 #############################
	def _createobject(self, typeid, nodeidx=1, nodetype=0, *args, **kwds):
		try:
			addrid, exposed, uid, logicid = dispatch_managerclass(self, 'createobject', (typeid,)+args, \
												kwds, nodeidx=nodeidx, nodetype=nodetype, typeid=typeid)
		except:
			LogPyException()
		return Token(typeid, nodetype, nodeidx, uid, addrid, logicid), exposed

	def _createobject2(self, typeid, nodeidx=1, nodetype=0, *args, **kwds):
		try:
			addrid, uid, logicid = dispatch_managerclass(self, 'createobject2', (typeid,)+args, \
										kwds, nodeidx=nodeidx, nodetype=nodetype, typeid=typeid)
		except:
			LogPyException()
		return Token(typeid, nodetype, nodeidx, uid, addrid, logicid)

	def _debug_info(self, nodeidx=1, nodetype=0):
		try:
			return dispatch_managerclass(self, 'debug_info', nodeidx=nodeidx, \
								nodetype=nodetype, typeid=type(self).__name__)
		except:
			LogPyException()

	def _number_of_objects(self, nodeidx=1, nodetype=0):
		try:
			return dispatch_managerclass(self, 'number_of_objects', nodeidx,\
									 nodetype, typeid=type(self).__name__)
		except:
			LogPyException()

	#@classmethod
	def register(self, typeid, callable=None, nodeidx=0, nodetype=NODE_TYPE_MAIN, \
			proxytype=None, exposed=None,method_to_typeid=None, create_method=True):
		if proxytype is None: proxytype = AutoProxy
		# if typeid=="AreaWar_SI":print "typeid>>>>>>>>>>>>>>>>",typeid, callable #if typeid=="AreaWar_SI":
		#exposed为代理类的成员方法集合
		exposed = exposed or getattr(proxytype, '_exposed_', None)
		method_to_typeid = method_to_typeid or \
						   getattr(proxytype, '_method_to_typeid_', None)

		if method_to_typeid:
			for key, value in method_to_typeid.items():
				assert type(key) is str, '%r is not a string' % key
				assert type(value) is str, '%r is not a string' % value
		# if typeid=="AreaWar_SI":print "typeid>>>>>>2222222222",typeid, callable #if typeid=="AreaWar_SI":
		self.classregistry[typeid] = (callable, exposed, method_to_typeid, proxytype)
		LogToFileWrap("proxymng", "[INFO]|client:register=%s,self=%s"%(typeid, callable))
		if create_method:
			#返回远程代理对象
			def temp(*args, **kwds):
				try:
					'''
					if IsSceneServer():
						raise Exception(Language("战斗服上不可执行创建代理%0$s！",typeid))
					global gProxyClientMng
					gMng = gProxyClientMng
					LogToFile("proxymng", "proxy creation of a shared %r object on nodes:(%r|%r)"%\
																		(typeid, nodeidx, nodetype))
					token, exp = kwds.get("token"), kwds.get("exposed")
					if not exp: exp = public_methods(callable) #exp函数列表本地创建不走网络传输
					if method_to_typeid is not None:
						assert type(method_to_typeid) is dict
						exp = list(exp) + list(method_to_typeid)
					boremote = True if not token else False #False则仅创建本地代理对象而已
					if boremote: #被动请求远程创建真实对象
						#token, exp = gMng._createobject(typeid, nodeidx, nodetype, *args, **kwds)
						token = gMng._createobject2(typeid, nodeidx, nodetype, *args, **kwds)

					proxy = proxytype(token, gMng._serializer, callable, manager=gMng, \
													authkey=gMng._authkey, exposed=exp)
					'''
					global gProxyClientMng
					gMng = gProxyClientMng
					proxy = gMng.createproxyabs(typeid, callable, proxytype, nodeidx,\
											 nodetype, method_to_typeid, *args, **kwds)
					#这里处理方法是：这里注释掉proxy._decref()的主要原因是：由于创建代理的过程中，
					#				 远程对象先+1，创建代理调用远程方法再+1，这样就有2个引用，
					#				 一个是远程的对象引用自己引用自己，一个是本地代理引用远程，
					#				 所以远程对象的引用计数应该是2，在object.remove的时候，才调用
					#				 object._decref()，跟multiprocess库不一致
					#proxy._decref()
					#proxy.SetProxyToken(token)
					#所有的代理都被注册到底层(本地代理对象也可以通过C_FindObject获得响应对象)，
	#				if type(proxy).__name__ in ['CPlayer',]:
	#					C_CreatePlayerObj(proxy.UUID(), proxy) 
	#				else:
	#					C_CreateObject(proxy.UUID(), proxy)
					return proxy
				except:
					LogPyException()
					LogToFile("proxyCreateProxyFail", "typeid=%s|nodeidx=%s|nodetype=%s|args=%s|kwds=%s"%\
														(typeid,nodeidx,nodetype,str(args),str(kwds)))

			temp.__name__ = typeid
			setattr(self, typeid, temp)

class ProxyServerManager(ProxyBaseManager):
	def __init__(self):
		ProxyBaseManager.__init__(self)
		self.addrid_to_obj = {}	#本地对象管理（通过str(id)作为索引，其实也可以通过底层的holder去索引）
		self.id_to_refcount = {}

	######################### 本地处理接口 #############################
	def createobject(self, typeid, *args, **kwds):
		callable, exposed, method_to_typeid, proxytype = self.classregistry[typeid]
		#不直接调用create方法，目的是为了复用tmp方法的创建流程，可广播创建反向代理
		#请求方会也会收到该广播请求，但是自己通过C_FindObject方法过滤掉该反向代理创建的请求
		clsobject = getattr(self, typeid)(*args, **kwds)
		token = clsobject.GetProxyToken()
		if exposed is None:
			ident = '%x'%id(clsobject)
			exposed = self.addrid_to_obj[ident][1]

		#被动创建真实对象，需要马上将对象摆到对应的容器，否则有可能报错
		if kwds.has_key("dparams"):
			dparams = kwds.get("dparams")
			self.addobjectctn(token.typeid, dparams, clsobject)

		return token.addrid, tuple(exposed), clsobject.UUID(), token.logicid

	def createobject2(self, typeid, *args, **kwds):
		callable, exposed, method_to_typeid, proxytype = self.classregistry[typeid]
		#不直接调用create方法，目的是为了复用tmp方法的创建流程，可广播创建反向代理
		#请求方会也会收到该广播请求，但是自己通过C_FindObject方法过滤掉该反向代理创建的请求
		clsobject = getattr(self, typeid)(*args, **kwds)
		token = clsobject.GetProxyToken()

		#被动创建真实对象，需要马上将对象摆到对应的容器，否则有可能报错
		if kwds.has_key("dparams"):
			dparams = kwds.get("dparams")
			self.addobjectctn(token.typeid, dparams, clsobject)
		LogToFileWrap("proxycreateobject2", "typeid=%s,args=%s,kwds=%s"%(typeid,str(args),str(kwds)))
		return token.addrid, clsobject.UUID(), token.logicid

	def createDirectly(self, typeid, *args, **kwds):
		try:
			callable, exposed, method_to_typeid, proxytype = \
					  self.classregistry[typeid]
			return callable(*args, **kwds)
		except:
			LogPyException()

	def create(self, typeid, *args, **kwds):
		try:
			callable, exposed, method_to_typeid, proxytype = \
					  self.classregistry[typeid]
			if callable is None:
				assert len(args) == 1 and not kwds
				obj = args[0]
			else:
				obj = callable(*args, **kwds)

			if exposed is None:
				exposed = []
			if method_to_typeid is not None:
				assert type(method_to_typeid) is dict
				exposed = list(exposed) + list(method_to_typeid)

			ident = '%x' % id(obj)  # convert to string because xmlrpclib
									# only has 32 bit signed integers
#			LogToFile("proxymng", "%s callable returned object with id %s"%(typeid, ident))
#
#			self.addrid_to_obj[ident] = (obj, set(exposed), method_to_typeid)
#			if ident not in self.id_to_refcount:
#				self.id_to_refcount[ident] = 0
#			self.incref(ident)
			self.wrap_object(obj, ident, typeid, exposed, method_to_typeid)
			return ident, tuple(exposed), obj.UUID(), obj.OwnerId()
		except:
			LogPyException()

	def create2(self, typeid, *args, **kwds):
		try:
			callable, exposed, method_to_typeid, proxytype = \
					  self.classregistry[typeid]
			if callable is None:
				assert len(args) == 1 and not kwds
				obj = args[0]
			else:
				obj = callable(*args, **kwds)

			if exposed is None:
				exposed = []
			if method_to_typeid is not None:
				assert type(method_to_typeid) is dict
				exposed = list(exposed) + list(method_to_typeid)

			ident = '%x' % id(obj)  # convert to string because xmlrpclib
									# only has 32 bit signed integers
#			LogToFile("proxymng", "%s callable returned object with id %s"%(typeid, ident))
#
#			self.addrid_to_obj[ident] = (obj, set(exposed), method_to_typeid)
#			if ident not in self.id_to_refcount:
#				self.id_to_refcount[ident] = 0
#			self.incref(ident)
			self.wrap_object(obj, ident, typeid, exposed, method_to_typeid)
			return ident, obj.UUID(), obj.OwnerId()
		except:
			LogPyException()

	#提供给不注册代理，也能使用代理机制的的接口（特殊业务使用，比如CVirtualScene）
	def wrap_object(self, obj, ident=None, typeid=None, exposed=None, method_to_typeid=None):
		if not ident: ident = '%x' % id(obj)
		if not typeid: typeid = type(obj).__name__
		if not exposed: exposed = public_methods(obj.__class__)
		LogToFileWrap("proxymng", "%s callable returned object with id %s"%(typeid, ident))
		self.addrid_to_obj[ident] = (obj, set(exposed), method_to_typeid)
		if ident not in self.id_to_refcount:
			self.id_to_refcount[ident] = 0
		self.incref(ident)
		import Trans; oGateWayObj = Trans.GetGatewayObj()
		fromnodetype, fromnodeidx = oGateWayObj.GetNodeType(),oGateWayObj.GetNodeIdx()
		token = Token(typeid, fromnodetype, fromnodeidx, obj.UUID(), ident, obj.OwnerId())
		obj.SetProxyToken(token)

	def ctnproxycall(self, *args, **kwds):
		ident = args[0]
		lmeth = args[1] #oPlayer.PokedexCtn.Query
		args = args[2:] #oPlayer.PokedexCtn.Query调用的具体参数
		#PrintLog(">>>>>[容器类调用] ctnproxycall(), ident=%s,lmeth=%s,args=%s"%(ident,lmeth,args))
		if not self.addrid_to_obj.has_key(ident):
			raise Exception(Language(">>>>>[容器类调用], ident=%0$s,lmeth=%1$s,args=%2$s",\
																ident,str(lmeth),str(args)))
		obj = self.addrid_to_obj[ident][0]
		objparam = [obj]
		objparam.extend(lmeth)
		#递归获取最终的函数对象，上述例子的Query函数对象
		funcobj = reduce(lambda obj, key: getattr(obj, key), objparam)
		if type(funcobj) == types.MethodType or type(funcobj) == types.FunctionType:
			result = funcobj(*args, **kwds)
		else:
			result = funcobj #如果最终递归取出来的是obj的属性，则直接这样返回
		#PrintLog(">>>>>[容器类调用] ctnproxycall(), ident=%s,result=%s"%(ident,result))
		return result

	def evproxycall(self, *args, **kwds):
		ident = args[0]
		realobject = args[1]
		eventkey = args[2]
		opername = args[3]
		instobj = args[4]
		funcname = args[5]
		PrintLog(">>>>>[事件类调用] evproxycall(), realobject=%s,args=%s"%(realobject,str(args[2:])))
		if not hasattr(realobject, eventkey):
			PrintLog(">>>>>[事件类调用]，对象该事件，realobject=%s,eventkey=%s,opername=%s"%\
														(type(realobject),eventkey,opername))
			raise Exception(Language(">>>>>[事件类调用]，对象该事件，realobject=%0$s,eventkey=%1$s,opername=%2$s",\
																				type(realobject),eventkey,opername))
		#smodule = args[4]
		#funcobj = import2(smodule)
		funcobj = getattr(instobj, funcname) if instobj else import2(funcname)
		funcargs = args[6:]
		eventobj = getattr(realobject, eventkey)
		#eventobj对事件对象做 +=、+-操作
		operfuncobj = getattr(eventobj, opername)
		operfuncobj(CFunctor(funcobj, *funcargs, **kwds))

	def callremotefunc(self, *args, **kwds):
		modulefunc = args[0]
		args = args[1:]
		#PrintLog(">>>>>[远程调用] callremotefunc(), func=%s,args=%s,kwds=%s"%(modulefunc,str(args),str(kwds)))
		funcobj = import2(modulefunc)
		return funcobj(*args, **kwds)

	def incref(self, ident):
		if None == self.id_to_refcount.get(ident):
			return
		self.id_to_refcount[ident] += 1

	def decref(self, ident, bc=False):
		if not self.id_to_refcount.get(ident):
			if 0 == self.id_to_refcount.get(ident):
				LogToFile("proxymng", '[waring] decref obj with id[%r], token=%s,ref=0'\
														%(ident,obj.GetProxyToken()))
			return
		try:
			obj = self.addrid_to_obj[ident][0]
			if not bc:
				#如果bc为False，则认为是代理请求实体消耗，引用计数减一，直到计数为0，才删除实体引用
				assert self.id_to_refcount[ident] >= 1
				self.id_to_refcount[ident] -= 1
				if self.id_to_refcount[ident] <= 0:
					del self.addrid_to_obj[ident], self.id_to_refcount[ident]
					LogToFileWrap("proxymng", '[not bc] decref obj with id[%r], token=%s,'\
														%(ident,obj.GetProxyToken()))
			else:
				#如果bc为True，则认为实体对象请求销毁，所以直接广播所有代理销毁
				self._destroyproxy(obj.GetProxyToken())
				del self.addrid_to_obj[ident], self.id_to_refcount[ident]
				LogToFileWrap("proxymng", '[yes bc] decref obj with id[%r], token=%s'\
													%(ident,obj.GetProxyToken()))
		except:
			LogPyException()

	def debug_info(self):
		try:
			result = []
			keys = self.addrid_to_obj.keys()
			keys.sort()
			for ident in keys:
				if ident != '0':
					result.append('  %s:	   refcount=%s\n	%s' %
								  (ident, self.id_to_refcount[ident],
								   str(self.addrid_to_obj[ident][0])[:75]))
			return '\n'.join(result)
		except:
			LogPyException()

	def number_of_objects(self):
		return len(self.addrid_to_obj) - 1	  # don't count ident='0'


	def fallback_getvalue(self, ident, obj):
		return obj

	def fallback_str(self, ident, obj):
		return str(obj)

	def fallback_repr(self, ident, obj):
		return repr(obj)

	fallback_mapping = {
		'__str__':fallback_str,
		'__repr__':fallback_repr,
		'#GETVALUE':fallback_getvalue
		}

	def dummy(self):
		pass

	def get_methods(self, token):
		return tuple(self.addrid_to_obj[token.addrid][1])

	######################### 远程处理接口 #############################
	#默认销毁事件广播给所有主服销毁代理
	def _destroyproxy(self, token, nodeidx=-1, nodetype=-1):
		import init
		if IsSlaveServer() or IsMainServer() and init.GetSlaveServerInfo():
			dispatch_managerclass(self, 'destroyproxy', (token, ), nodeidx=nodeidx,\
			 					nodetype=nodetype,boWait=False,typeid=token.typeid) 

	#默认创建事件广播给所有主服创建代理（除本节点之前）
	def _createproxy(self, token, exposed, dparams, nodeidx=-1, nodetype=-1, obj=None, args=(), kwds={}):
		import init
		if IsSlaveServer() or IsMainServer() and init.GetSlaveServerInfo(): 
			dispatch_managerclass(self, 'createproxy', (token, exposed, dparams,), \
				nodeidx=nodeidx, nodetype=nodetype,boWait=False,typeid=token.typeid)
		if None<>obj: obj.AfterProxyCreate(*args, **kwds)

	def _createproxy2(self, token, dparams, nodeidx=-1, nodetype=-1, obj=None, args=(), kwds={}):
		import init
		if IsSlaveServer() or IsMainServer() and init.GetSlaveServerInfo(): 
			dispatch_managerclass(self, 'createproxy2', (token, dparams,), \
				nodeidx=nodeidx, nodetype=nodetype,boWait=False,typeid=token.typeid) 
		if None<>obj: obj.AfterProxyCreate(*args, **kwds)

	def register(self, typeid, callable=None, nodeidx=-1, nodetype=-1, proxytype=None, exposed=None,
				 method_to_typeid=None, create_method=True, delayproxy=False):
		'''
		Register a typeid with the manager type
		'''
		if proxytype is None:
			proxytype = AutoProxy

		#exposed为代理类的成员方法集合
		exposed = exposed or getattr(proxytype, '_exposed_', None)
		method_to_typeid = method_to_typeid or \
						getattr(proxytype, '_method_to_typeid_', None)

		if method_to_typeid:
			for key, value in method_to_typeid.items():
				assert type(key) is str, '%r is not a string' % key
				assert type(value) is str, '%r is not a string' % value

		self.classregistry[typeid] = (callable, exposed, method_to_typeid, proxytype)
		LogToFileWrap("proxymng", "[INFO]|server:register=%s,self=%s"%(typeid, callable))
		if create_method:
			#创建当前类的外部可动态传进（tonodeidx、tonodetype），当前创建的对象，需要被那些节点代理
			def temp(*args, **kwds):
				try:
					global gProxyServerMng; gMng = gProxyServerMng
					tonodeidx, tonodetype, dparams = nodeidx, nodetype, None
					if None <> kwds.get("tonodeidx"):
						tonodeidx = kwds.get("tonodeidx")
						del kwds["tonodeidx"]
					if None <> kwds.get("tonodetype"):
						tonodetype = kwds.get("tonodetype")
						del kwds["tonodetype"]
					if None <> kwds.get("dparams"):
						dparams = kwds.get("dparams")
						del kwds["dparams"]
					
					if IsSlaveServer() or IsMasterServerWithSlave():
						LogToFileWrap("proxymng", 'object creation of a shared %r object to nodes:(%r|%r)'%\
																			(typeid, nodeidx, nodetype))
						#将节点类型|节点索引|对象UID|对象地址，广播给其他主服，创建相关反向代理
						#创建本地真实对象
						#ident, texp, uid = gMng.create(typeid, *args, **kwds)
						ident, uid, logicid = gMng.create2(typeid, *args, **kwds)
						obj = gMng.addrid_to_obj[ident][0]
						if dparams: obj.SetProxyParams(dparams)
						token = obj.GetProxyToken()
						#gMng._createproxy(token, texp, dparams, nodeidx=tonodeidx,\
						#			nodetype=tonodetype, obj=obj, args=args, kwds=kwds)
						if not delayproxy:
							gMng._createproxy2(token, dparams, nodeidx=tonodeidx,\
								nodetype=tonodetype, obj=obj, args=args, kwds=kwds)
					else:
						obj = gMng.createDirectly(typeid, *args, **kwds)
					return obj
				except:
					LogPyException()
					LogToFile("proxyCreateObjectFail", "typeid=%s|token=%s|args=%s|kwds=%s"%\
															(typeid,token,str(args),str(kwds)))

			temp.__name__ = typeid
			setattr(self, typeid, temp)

#CPlayer类的身上各个模块的Ctn容器以及Cyc周期容器代理类
class CtnProxy(object):
	def __init__(self, skey, proxyhost):
		self._lkey = [skey,]
		self._proxy = weakref.ref(proxyhost)

	#覆盖__getattr__的方法，当访问不存在的属性的时候，就触发这个函数调用
	def __getattr__(self, skey):
		self._lkey.append(skey)
		return self
	def __call__(self, *args, **kwds):
		token = self._proxy().token
		_callmethodmng = object.__getattribute__(self._proxy(), "_callmethodmng")
		return _callmethodmng("ctnproxycall", (self._lkey,)+args, kwds)

#远程事件
class EventObjProxy(CEvent):
	def __init__(self, proxyobj, eventkey):
		#super(CEvent, self).__init__()
		self._proxy = weakref.ref(proxyobj) #事件发送者sender
		self._eventkey = eventkey 			#事件key名

	def _icalc(self, opername, handler):
		funcname, instobj, args = _func_parse(handler)
		#import traceback
		#callstack=str(traceback.format_stack())
		_callmethodmng = object.__getattribute__(self._proxy(), "_callmethodmng")
		#参数传输:事件接收代理对象+事件键名+操作函数名称+[实例对象]+模块名.[类名].类成员函数名称+函数列表1+函数列表2
		_callmethodmng("evproxycall", (self._proxy(), self._eventkey, opername, instobj, funcname)+args, {})

	def __iadd__(self, handler):
		#CEvent.__iadd__(self, handler)
		self._icalc("__iadd__", handler)
		return self

	def __isub__(self, handler):
		#CEvent.__iadd__(self, handler)
		self._icalc("__isub__", handler)
		return self

#
# Definition of BaseProxy
#

class BaseProxy(CObject):
	'''
	A base for proxies of shared objects
	'''
	#_address_to_local = {}

	def __init__(self, token, serializer, cls=None, manager=None,
						authkey=None, exposed=None, incref=True):

		self._token = token
		self._remotetype = self._token.nodetype 	#远程节点类型
		self._remoteno = self._token.nodeidx 		#远程节点编号
		self._uid = self._token.uid 				#远程对象UID
		self._addrid = self._token.addrid 			#远程对象地址ID
		self._manager = manager
		self._serializer = serializer
		self._isauto = True

		if authkey is not None:
			self._authkey = AuthenticationString(authkey)
		elif self._manager is not None:
			self._authkey = self._manager._authkey

		#初始化基类
#		lbasecls = self.__class__.mro()
#		for i in reversed(xrange(1, len(lbasecls))):
#			lbasecls[i].__init__(self)

		#创建代理对象的时增加远程对象引用计数
		if incref: self._incref()
		self._manager.addProxy(self._uid, self)
		self.SetProxyToken(token)

	#覆盖__getattr__的方法，当访问不存在的属性的时候，就触发这个函数调用
	def __getattr__(self, key, *args):
		lCls = self.__class__.mro()
		if key[0] == "_" or key in glUseLocalAttribute or key in glUseLocalMethodExe\
		or "%s.%s"%(lCls[0].__name__,key) in glUseLocalAttribute:
			try:
				return object.__getattribute__(self, key)
			except:
				if key == "iUID":
					return self._token.uid
				else:
					return args[0]
			raise Exception("当前分支必须在本地执行，self=%s,key=%s,args=%s"%(self,key,str(args)))
		elif key[-3:] == "Ctn" or key[0:3] == "Cyc":#角色容器类和角色周期容器属性特殊处理
			#lCls = self.__class__.mro()
			if lCls[0].__name__ in glAccessCycCtnClsName:
				return CtnProxy(key, self)
		elif key[0]=="e" and key[1].isupper(): 		#事件对象key处理
			try:
				return object.__getattribute__(self, key)
			except:
				eventproxy = EventObjProxy(self, key)
				object.__setattr__(self, key, eventproxy)
				return eventproxy

		callmethod = object.__getattribute__(self, "_callmethod")
		return callmethod("__getattribute__", (key,)+args)

	#覆盖__getattr__的方法，当访问属性不管存在与否，就触发这个函数调用
	def __setattr__(self, key, value):
		lCls = self.__class__.mro()
		if key[0] == "_" or key in glUseLocalAttribute or key in glUseLocalMethodExe\
		or "%s.%s"%(lCls[0].__name__,key) in glUseLocalAttribute:
			return object.__setattr__(self, key, value)
		elif key[-3:] == "Ctn":
			#lCls = self.__class__.mro()	#角色容器类属性特殊处理
			if lCls[0].__name__ in glAccessCycCtnClsName:
				raise Exception(Language("不要对代理代理类点SET操作,继承关系=%s，键=%s，值=%s"%\
																		(self.mro(),key,value)))
		elif key[0]=="e" and key[1].isupper() and type(value)==EventObjProxy: #事件对象key处理
			return #+=/-=之后的set操作，直接return
		PrintLog(">>>>> [警告！！]，不建议你对代理对象直接进行点SET操作,type=%s,key=%s,value=%s"%\
																	(type(self),key,value))
		LogToFile("proxySetWarning", "__setattr__|type=%s|key=%s|value=%s"%(type(self),key,value))
		callmethod = object.__getattribute__(self, "_callmethod")
		return callmethod("__setattr__", (key, value))

	def __delattr__(self, key):
		lCls = self.__class__.mro()
		if key[0] == "_" or key in glUseLocalAttribute or "%s.%s"%(lCls[0].__name__,key) in glUseLocalAttribute:
			return object.__delattr__(self, key)
		LogToFile("proxySetWarning", "__delattr__|type=%s|key=%s"%(type(self),key))
		callmethod = object.__getattribute__(self, "_callmethod")
		return callmethod("__delattr__", (key,))

	def _callmethod(self, methodname, args=(), kwds={}):
		try:
			kind, result = dispatch_proxyobject(self._manager, self._token, methodname, args, kwds)
		except:
			LogToFile("proxyCallError", "_token=%s,methodname=%s,args=%s,kwds=%s"%(self._token,methodname,str(args),str(kwds)))
			LogPyException()
			raise Exception("异常调用_callmethod()终止流程！！token=%s,methodname=%s,args=%s,kwds=%s"%(self._token,methodname,str(args),str(kwds)))
		if kind == '#RETURN':
			return result
		elif kind == '#PROXY':
			#周通用的反向代理创建流程代理对象已经先于此创建好
			token = result
			return C_FindObject(token.uid)
		raise convert_to_error(kind, result, "_callmethod", self._token.addrid, \
									self._token.uid, self._token.typeid, methodname)

	def _callmethodmng(self, methodname, args=(), kwds={}, boWait=True):
		result = dispatch_managerclass(self._manager, methodname, (self._addrid,)+args, kwds, \
									nodeidx=self._token.nodeidx, nodetype=self._token.nodetype,\
									boWait=boWait,typeid=self._token.typeid)
		#LogToFile("proxymng", "DEBG|_callmethodmng(%s) addrid:%r, uid:%s"%(methodname, self._addrid, self._uid))
		return result

	#Get a copy of the value of the referent
	def _getvalue(self):
		return self._callmethod('#GETVALUE')

	def _incref(self):
		dispatch_managerclass(self._manager, "incref", (self._addrid,), nodeidx=self._token.nodeidx, 
							nodetype=self._token.nodetype,boWait=False,typeid=self._token.typeid)
		#LogToFile("proxymng", "DEBG|_incref() addrid:%r, uid:%s"%(self._addrid, self._uid))

	def _decref(self):
		dispatch_managerclass(self._manager, "decref", (self._addrid,), nodeidx=self._token.nodeidx, 
								nodetype=self._token.nodetype,boWait=True,typeid=self._token.typeid)
		#LogToFile("proxymng", "DEBG|_decref() addrid:%r, uid:%s"%(self._addrid, self._uid))

#	@staticmethod
#	def _decref(token, authkey=''):
#		#dispatch_proxyobject(token, "decref", (token.addrid,))
#		dispatch_managerclass(self, "decref", (token.addrid,), nodeidx=token.nodeidx,
#										nodetype=token.nodetype,boWait=False)
#		LogToFile("proxymng", "DEBG|DECREF addrid:%r, uid:%s"%(token.addrid, token.uid))

#	看RebuildProxy的实现用于unpick代理对象使用，暂时应该用不上
#	def __reduce__(self):
#		kwds = {}
#		if Popen.thread_is_spawning():
#			kwds['authkey'] = self._authkey
#
#		if getattr(self, '_isauto', False):
#			kwds['exposed'] = self._exposed_
#			return (RebuildProxy,
#					(AutoProxy, self._token, self._serializer, kwds))
#		else:
#			return (RebuildProxy,
#					(type(self), self._token, self._serializer, kwds))

	def __deepcopy__(self, memo):
		return self._getvalue()

	def __repr__(self):
		return '<%s object, typeid %r at %s>' % \
			   (type(self).__name__, self._token.typeid, '0x%x' % id(self))

	def __str__(self):
		'''
		Return representation of the referent (or a fall-back if that fails)
		'''
#		try:
#			return self._callmethod('__repr__')
#		except Exception:
#			return repr(self)[:-1] + "; '__str__()' failed>"
		return repr(self)

#
# Function used for unpickling
#

#def RebuildProxy(func, token, serializer, kwds):
#	'''
#	Function used for unpickling proxy objects.
#
#	If possible the shared object is returned, or otherwise a proxy for it.
#	'''
#	server = getattr(current_process(), '_manager_server', None)
#
#	if server and server.address == token.address:
#		return server.addrid_to_obj[token.addrid][0]
#	else:
#		incref = (
#			kwds.pop('incref', True) and
#			not getattr(current_process(), '_inheriting', False)
#			)
#		return func(token, serializer, incref=incref, **kwds)

#
# Functions to create proxies and proxy types
#

#传入函数成员列表exposed，返回代理类对象
if not globals().has_key("gdExposeCache"):
	gdExposeCache = {}
def MakeProxyType(name, exposed, cls):
	global gdExposeCache
	#exposed = tuple(exposed)
	modName = "%s.%s"%(cls.__module__,cls.__name__)
	try:
		#return gdExposeCache[(name, exposed)]
		return gdExposeCache[modName][0]
	except KeyError:
		pass

	dic = {}
	exposed = exposed if exposed else public_methods(cls)
	for meth in exposed:
		if meth not in glUseLocalMethodNoExec:
			exec '''def %s(self, *args, **kwds):
			return self._callmethod(%r, args, kwds)''' % (meth, meth) in dic
		else:
			exec '''def %s(self, *args, **kwds):
			pass''' % (meth,) in dic

	#构造代理类（继承关系都包含进来，让相关类的成员方法也包括进来，
	#需要远程访问的方法，则需要添加到exposed中）
	lcls = [BaseProxy,]
	lcls.extend(cls.mro())
#	ProxyType = type(name, (BaseProxy,), dic)
	ProxyType = type(name, tuple(lcls), dic)
	ProxyType._exposed_ = exposed
	#gdExposeCache[(name, exposed)] = ProxyType
	gdExposeCache[modName] = [ProxyType, exposed]
	LogToFile("proxyHitCache", "not hit|name=%s,modName=%s,ProxyType=%s"%(name, modName, ProxyType))
	return ProxyType

#更新代理类的成员方法（主要用于热更之后可能对原有的类的成员函数的变动）
def UpdateProxyType(cls):
	global gdExposeCache
	modName = "%s.%s"%(cls.__module__,cls.__name__)
	if not gdExposeCache.has_key(modName): return
	
	ProxyType, old_exposed = gdExposeCache[modName][0], gdExposeCache[modName][1]
	new_exposed = public_methods(cls)
	diff_list = list(set(new_exposed).difference(old_exposed))
	if diff_list: Print(">>> 【热更差异】 UpdateProxyType(), diff_list=%s"%diff_list)
	if diff_list:
		dic = {}
		for meth in diff_list:
			if meth not in glUseLocalMethodNoExec:
				exec '''def %s(self, *args, **kwds):
				return self._callmethod(%r, args, kwds)''' % (meth, meth) in dic
			else:
				exec '''def %s(self, *args, **kwds):
				pass''' % (meth,) in dic
		for meth, methobj in dic.iteritems():
			if meth <> '__builtins__':
				#新增的成员函数则插入（热更机制里面只有新增函数，没有删除原有函数，所以这里也不处理删除情况）
				if meth not in ProxyType.__dict__:
					setattr(ProxyType, meth, methobj)
				else:
					#delattr(ProxyType, meth)
					pass

#传入token生成代理类对象
def AutoProxy(token, serializer, cls, manager=None, authkey=None,
			  exposed=None, incref=True):
#	if exposed is None:
#		try:
#			#todo：这里可优化下，读取本地，不用再兜转去远程拿当前类的方法
#			exposed = dispatch_managerclass(manager, 'get_methods', (token,), \
#				nodeidx=token.nodeidx, nodetype=token.nodetype,typeid=token.typeid)
#		except:
#			LogPyException()

	if authkey is None and manager is not None:
		authkey = manager._authkey

	#ProxyType = MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
	#直接用原生的类名如：CPlayer，兼容 isinstance(oPlayer, player.CPlayer)的写法，
	#代理类也可以这样直接判断who是否为CPlayer类
	proxy = None
	if not manager.isExistProxy(token.uid):
		ProxyType = MakeProxyType(token.typeid, exposed, cls)
		proxy = ProxyType(token, serializer, cls=None, manager=manager, 
							authkey=authkey, incref=incref)
	else:
		#在上面调用get_methods异步回来，其实远程已经请求本地创建了代理
		#所以这里需要判断proxy是否已存在，存在则直接取本地缓存的代理对象
		proxy = manager.getProxy(token.uid)
	#proxy._isauto = True
	return proxy

#def ManualProxy(token, serializer, cls, manager=None, authkey=None,
#			  exposed=None, incref=True):
#	if exposed is None:
#		try:
#			exposed = dispatch_managerclass(manager, 'get_methods', (token,), \
#				nodeidx=token.nodeidx, nodetype=token.nodetype,typeid=token.typeid)
#		except:
#			LogPyException()
#
#	if authkey is None and manager is not None:
#		authkey = manager._authkey
#
#	#ProxyType = MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
#	#直接用原生的类名如：CPlayer，兼容 isinstance(oPlayer, player.CPlayer)的写法，
#	#代理类也可以这样直接判断who是否为CPlayer类
#	ProxyType = MakeProxyType(token.typeid, exposed, cls)
#	proxy = ProxyType(token, serializer, manager=manager, 
#						authkey=authkey, incref=incref)
#	proxy._isauto = True
#	return proxy

#
# Types/callables which we will register with SyncManager
#

class Namespace(object):
	def __init__(self, **kwds):
		self.__dict__.update(kwds)
	def __repr__(self):
		items = self.__dict__.items()
		temp = []
		for name, value in items:
			if not name.startswith('_'):
				temp.append('%s=%r' % (name, value))
		temp.sort()
		return 'Namespace(%s)' % str.join(', ', temp)

class Value(object):
	def __init__(self, typecode, value, lock=True):
		self._typecode = typecode
		self._value = value
	def get(self):
		return self._value
	def set(self, value):
		self._value = value
	def __repr__(self):
		return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value)
	value = property(get, set)

#def Array(typecode, sequence, lock=True):
#	return array.array(typecode, sequence)

#
# Proxy types used by SyncManager
#

class IteratorProxy(BaseProxy):
	# XXX remove methods for Py3.0 and Py2.6
	_exposed_ = ('__next__', 'next', 'send', 'throw', 'close')
	def __iter__(self):
		return self
	def __next__(self, *args):
		return self._callmethod('__next__', args)
	def next(self, *args):
		return self._callmethod('next', args)
	def send(self, *args):
		return self._callmethod('send', args)
	def throw(self, *args):
		return self._callmethod('throw', args)
	def close(self, *args):
		return self._callmethod('close', args)


class AcquirerProxy(BaseProxy):
	_exposed_ = ('acquire', 'release')
	def acquire(self, blocking=True):
		return self._callmethod('acquire', (blocking,))
	def release(self):
		return self._callmethod('release')
	def __enter__(self):
		return self._callmethod('acquire')
	def __exit__(self, exc_type, exc_val, exc_tb):
		return self._callmethod('release')


class ConditionProxy(AcquirerProxy):
	# XXX will Condition.notfyAll() name be available in Py3.0?
	_exposed_ = ('acquire', 'release', 'wait', 'notify', 'notify_all')
	def wait(self, timeout=None):
		return self._callmethod('wait', (timeout,))
	def notify(self):
		return self._callmethod('notify')
	def notify_all(self):
		return self._callmethod('notify_all')

class EventProxy(BaseProxy):
	_exposed_ = ('is_set', 'set', 'clear', 'wait')
	def is_set(self):
		return self._callmethod('is_set')
	def set(self):
		return self._callmethod('set')
	def clear(self):
		return self._callmethod('clear')
	def wait(self, timeout=None):
		return self._callmethod('wait', (timeout,))

class NamespaceProxy(BaseProxy):
	_exposed_ = ('__getattribute__', '__setattr__', '__delattr__')
	def __getattr__(self, key):
		if key[0] == '_':
			return object.__getattribute__(self, key)
		callmethod = object.__getattribute__(self, '_callmethod')
		return callmethod('__getattribute__', (key,))
	def __setattr__(self, key, value):
		if key[0] == '_':
			return object.__setattr__(self, key, value)
		callmethod = object.__getattribute__(self, '_callmethod')
		return callmethod('__setattr__', (key, value))
	def __delattr__(self, key):
		if key[0] == '_':
			return object.__delattr__(self, key)
		callmethod = object.__getattribute__(self, '_callmethod')
		return callmethod('__delattr__', (key,))


class ValueProxy(BaseProxy):
	_exposed_ = ('get', 'set')
	def get(self):
		return self._callmethod('get')
	def set(self, value):
		return self._callmethod('set', (value,))
	value = property(get, set)


BaseListProxy = MakeProxyType('BaseListProxy', (
	'__add__', '__contains__', '__delitem__', '__delslice__',
	'__getitem__', '__getslice__', '__len__', '__mul__',
	'__reversed__', '__rmul__', '__setitem__', '__setslice__',
	'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
	'reverse', 'sort', '__imul__'
	),list)				  # XXX __getslice__ and __setslice__ unneeded in Py3.0
class ListProxy(BaseListProxy):
	def __iadd__(self, value):
		self._callmethod('extend', (value,))
		return self
	def __imul__(self, value):
		self._callmethod('__imul__', (value,))
		return self


BaseDictProxy = MakeProxyType('BaseDictProxy', (
	'__contains__', '__delitem__', '__getitem__', '__len__',
	'__setitem__', 'clear', 'copy', 'get', 'has_key', 'items',
	'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'
	),dict)
class DictProxy(BaseDictProxy):
	def iteritems(self):
		return self._callmethod('items')
	def iterkeys(self):
		return self._callmethod('keys')

#ArrayProxy = MakeProxyType('ArrayProxy', (
#	'__len__', '__getitem__', '__setitem__', '__getslice__', '__setslice__'
#	),Array)				  # XXX __getslice__ and __setslice__ unneeded in Py3.0


#PoolProxy = MakeProxyType('PoolProxy', (
#	'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join',
#	'map', 'map_async', 'terminate'
#	),Pool)
#PoolProxy._method_to_typeid_ = {
#	'apply_async': 'AsyncResult',
#	'map_async': 'AsyncResult',
#	'imap': 'Iterator',
#	'imap_unordered': 'Iterator'
#	}


if not globals().has_key("gCoMng"):
	gCoMng = CoroutineWrap()
if not globals().has_key("gProxyClientMng"):
	gProxyClientMng = ProxyClientManager()
if not globals().has_key("gProxyServerMng"):
	gProxyServerMng = ProxyServerManager()

class test(CObject):
	def __init__(self, param):
		CObject.__init__(self)
		self.a = 11111
		self.param = param

	def getA(self):
		return self.a

	def setA(self, a):
		self.a = a

	def getParam(self):
		return self.param

	def myprint(self):
		PrintLog("hello world")

	def showtoken(self):
		PrintLog("====>> token:%s"%self.GetProxyToken())
		return self.GetProxyToken()

def AfterFirstImport():
	global gProxyClientMng
	global gProxyServerMng
	#import guid
	#gProxyClientMng.register('test', test, nodeidx=666, nodetype=2)
	#gProxyServerMng.register('test', test)
	#gProxyClientMng.register('CGuidGenerator', guid.CGuidGenerator, nodeidx=0,nodetype=0)
	#gProxyServerMng.register('CGuidGenerator', guid.CGuidGenerator)

def OnHotUpdate():
	global gProxyClientMng
	global gProxyServerMng
	gProxyClientMng.register_handle_method_by_cache()
	gProxyServerMng.register_handle_method_by_cache()

class mylist(list, CBaseProxyObject):
	def __init__(self, *args, **kwds):
		CBaseProxyObject.__init__(self)
		list.__init__(self, *args, **kwds)

class mydict(dict, CBaseProxyObject):
	def __init__(self, *args, **kwds):
		CBaseProxyObject.__init__(self)
		dict.__init__(self, *args, **kwds)

#基础类代理注册代理类
gProxyClientMng.register('list', mylist, proxytype=ListProxy)
gProxyServerMng.register('list', mylist, proxytype=ListProxy)

gProxyClientMng.register('dict', mydict, proxytype=DictProxy)
gProxyServerMng.register('dict', mydict, proxytype=DictProxy)

#默认对外注册代理类接口
#boOnlyServer:仅仅注册到Server容器，Client容器不注册，让部分类执行的时报错
#tonodeidx和tonodetype指定创建代理到哪个节点上（在某种特殊情况，真实对象在从服上，创建代理
#	到主服，仅作为函数调用时，可以支持对象传输而已，定义对象传输，可定义在gdObjectGetFuncMap上）
#delayproxy:是否在延后才决定创建代理（不在构造过程创建，而是在完成构造之后才触发创建代理）
def registerdefault(typeid, callable, proxytype=None, boOnlyServer=False, \
					tonodeidx=-1, tonodetype=-1,delayproxy=False,lTransOpt=1):
	if not boOnlyServer: gProxyClientMng.register(typeid, callable, proxytype=proxytype)
	gProxyServerMng.register(typeid, callable, proxytype=proxytype, nodeidx=tonodeidx, \
											nodetype=tonodetype,delayproxy=delayproxy)
	regProxyTransFunc(callable, typeid, lTransOpt)

def UpdateModProxyType(module):
	global gProxyClientMng
	moduleName = module.__name__
	classregistry = gProxyClientMng.classregistry
	for typeid, lclassinfo in classregistry.iteritems():
		callable = lclassinfo[0]
		if moduleName == callable.__module__:
			UpdateProxyType(callable)

def getcallable(typeid, boServer=True):
	# try:
	if boServer:
		global gProxyServerMng
		callable = gProxyServerMng.classregistry[typeid][0]
	else:
		global gProxyClientMng
		callable = gProxyClientMng.classregistry[typeid][0]
	return callable
	# except:
	# 	LogPyException()
	# 	if boServer:
	# 		raise Exception(Language("未注册类%0$s到gProxyServerMng",typeid))
	# 	else:
	# 		raise Exception(Language("未注册类%0$s到gProxyClientMng",typeid))

#args：不定参数里面肯定包含一个远程代理对象，
#否则需要在kwds指定需要跟远程通讯的节点类型和索引（或编号）
#	备注：详细看remotecall函数的注释
def remotemodulefunc(*args, **kwds):
	global gProxyServerMng
	global gProxyClientMng

	nodeidx, nodetype = -1, -1
	boCallLocal = kwds.get("boCallLocal", False)
	if None <> kwds.get("nodetype") or None <> kwds.get("nodeidx"):
		nodeidx = kwds.get("nodeidx")
		nodetype = kwds.get("nodetype")
	else:
		boTarCheck = False
		proxyobj = None
		#优先读取指定代理对象位置的参数 iTarParamIdx 或者 sTarParamName
		if kwds.has_key("iTarParamIdx") and None <> kwds["iTarParamIdx"]:
			boTarCheck = True
			uid = args[kwds["iTarParamIdx"]]
			proxyobj = C_FindObject(uid) if type(uid)==long or type(uid)==int else uid
		if not proxyobj and kwds.has_key("sTarParamName") and None <> kwds["sTarParamName"]:
			boTarCheck = True
			sTarParamName = kwds["sTarParamName"]
			if kwds.has_key(sTarParamName):
				uid = kwds[sTarParamName]
				proxyobj = C_FindObject(uid) if type(uid)==long or type(uid)==int else uid
		if proxyobj:
			nodeidx = proxyobj._token.nodeidx
			nodetype = proxyobj._token.nodetype
		else: 
			if not boTarCheck:
				for arg in args[0:len(args)-1]:
					if isinstance(arg, CBaseProxyObject) and arg.boProxy():
						proxyobj = arg
						break
				if not proxyobj:
					for k, v in kwds.iteritems():
						if isinstance(v, CBaseProxyObject) and v.boProxy():
							proxyobj = v
							break
				if proxyobj:
					nodeidx = proxyobj._token.nodeidx
					nodetype = proxyobj._token.nodetype
				else:
					PrintLog(">>>>> remotemodulefunc|参数异常：args=%s,kwds=%s"%(str(args),str(kwds)))
					LogToFile("proxyRemoteCallErr", "remotemodulefunc|参数异常：args=%s,kwds=%s"%(str(args),str(kwds)))
					raise Exception(Language("remotemodulefunc|参数异常：args=%0$s,kwds=%1$s",str(args),str(kwds)))
			elif boCallLocal:
				#如果指定参数都找不到的proxyobj，则认为who不在线，框架层自动调用本地函数
				global gdRemoteModuleMethod
				kwds_t = copy.deepcopy(kwds)
				if kwds_t.has_key("boWait"): del kwds_t["boWait"]
				if kwds_t.has_key("callserver"): del kwds_t["callserver"]
				if kwds_t.has_key("nodeidx"): del kwds_t["nodeidx"]
				if kwds_t.has_key("nodetype"): del kwds_t["nodetype"]
				if kwds_t.has_key("iTarParamIdx"): del kwds_t["iTarParamIdx"]
				if kwds_t.has_key("sTarParamName"): del kwds_t["sTarParamName"]
				if kwds_t.has_key("boBc"): del kwds_t["boBc"]
				if kwds_t.has_key("boCallLocal"): del kwds_t["boCallLocal"]
				localfuncobj = gdRemoteModuleMethod[args[-1]]
				return localfuncobj(*args[0:len(args)-1], **kwds_t)

	modulefuncname = args[-1]
	boWait = kwds.get("boWait", True)
	callserver = True #这里跟请求的真实对象所在的节点无关，kwds.get("callserver", True)

	if kwds.has_key("boWait"): del kwds["boWait"]
	if kwds.has_key("callserver"): del kwds["callserver"]
	if kwds.has_key("nodeidx"): del kwds["nodeidx"]
	if kwds.has_key("nodetype"): del kwds["nodetype"]
	if kwds.has_key("iTarParamIdx"): del kwds["iTarParamIdx"]
	if kwds.has_key("sTarParamName"): del kwds["sTarParamName"]
	if kwds.has_key("boCallLocal"): del kwds["boCallLocal"]
	#Print("【请求远程模块函数】：nodeidx=%s,nodetype=%s,args=%s"%(nodeidx,nodetype,str(args)))
	gMng = gProxyClientMng if callserver else gProxyServerMng
	remoteRet = dispatch_managerclass(gMng, "modulefunccall", args, kwds, \
								nodeidx, nodetype, boWait, modulefuncname)
	if not boWait and boCallLocal:
		if kwds.has_key("boBc"): del kwds["boBc"]
		localRet = gdRemoteModuleMethod[modulefuncname](*args[0:len(args)-1], **kwds)
		return localRet
	else:
		return remoteRet

def getoldmodulefunc(modulefuncname):
	global gdRemoteModuleMethod
	return gdRemoteModuleMethod[modulefuncname]

#注册当前模块
#funcobj:需要被远程调用的原模块函数对象
#nodetype:远程目标服务节点类型（定义在utility.py中 NODE_TYPE_XXX）
#nodeidx:远程目标服务节点索引
#boWait:是否需要协程挂起等待结果返回
#boBc:是否需要广播调用（远程那边只有1个结果返回就触发本地挂起的协程苏醒）
#iTarParamIdx:指定args那个位置的参数具有token的对象（有些参数没有传who而是传了rid，
#			  所以通过指定第几个参数来C_FindObject获取who的实体对象）
#sTarParamName:指定kwds哪个键名的参数具有token的对象（同上）
#boCallLocal:是否同时调用本地函数（boCallLocal等于True，同时当找不到who、或boWait等于False时自动切回调用本地函数）
def registermodulefunc(funcobj,nodetype=None,nodeidx=None,boWait=True,boBc=False,\
							iTarParamIdx=None,sTarParamName=None,boCallLocal=False):
	if isinstance(funcobj, CFunctor):
		return
	global gdRemoteModuleMethod
	modulename = funcobj.__module__
	funcname = funcobj.__name__
	modulefuncname = "%s.%s"%(modulename, funcname)
	if gdRemoteModuleMethod.has_key(modulefuncname):
		return

	if not (type(funcobj) == types.FunctionType or type(funcobj) == types.MethodType):
		LogPyException()
		raise Exception(Language("获取出来的不是函数对象, type(funcobj)==%s"%type(funcobj)))

	tmp = CFunctor(remotemodulefunc, modulefuncname, nodetype=nodetype, nodeidx=nodeidx,boWait=boWait,\
				boBc=boBc,iTarParamIdx=iTarParamIdx,sTarParamName=sTarParamName,boCallLocal=boCallLocal)
#	def tmp(*args, **kwds):
#		run(*args, **kwds)
	def bakup(*args, **kwds):
		pass

	#将需要备份的函数对象funcobj函数对象，拷贝一份信息过去新的函数对象tmp
	#dealFunction(bakup, funcobj)

	#原来的函数对象备份在gdRemoteModuleMethod里面
	gdRemoteModuleMethod[modulefuncname] = funcobj

	#用registermodulefunc替换原来的函数funcobj
	try:
		setattr(import1(modulename), funcname, tmp)
		setattr(tmp, "localfunc", funcobj)
	except:
		#出错恢复并且打印堆栈
		setattr(import1(modulename), funcname, gdRemoteModuleMethod[modulefuncname])
		del gdRemoteModuleMethod[modulefuncname]
		LogPyException()

def unregistermodulefunc(modulefuncname):
	global gdRemoteModuleMethod
	if not gdRemoteModuleMethod.has_key(modulefuncname):
		return
	modlist = modulefuncname.split(".")
	lmod = modlist[0:len(modlist)-1]
	mod = import1(".".join(lmod))
	funcname = modlist[-1]
	bakup = gdRemoteModuleMethod[modulefuncname]
	del gdRemoteModuleMethod[modulefuncname]

	newfuncobj = getattr(mod, funcname)
	if not newfuncobj:
		raise Exception(Language("new FUNCTION:%s not EXIST!!"%modulefuncname))
	#dealFunction(newfuncobj, bakup)
	setattr(mod, funcname, bakup)
	#del newfuncobj._modulefuncname

#import guid
#gProxyClientMng.register('test', test, nodeidx=666, nodetype=2)
#gProxyServerMng.register('test', test)
#gProxyClientMng.register('CGuidGenerator', guid.CGuidGenerator, nodeidx=0,nodetype=0)
#gProxyServerMng.register('CGuidGenerator', guid.CGuidGenerator)


#
# Definition of SyncManager
#

#class SyncManager(BaseManager):
#	'''
#	Subclass of `BaseManager` which supports a number of shared object types.
#
#	The types registered are those intended for the synchronization
#	of threads, plus `dict`, `list` and `Namespace`.
#
#	The `multiprocessing.Manager()` function creates started instances of
#	this class.
#	'''
#
#SyncManager.register('Queue', Queue.Queue)
#SyncManager.register('JoinableQueue', Queue.Queue)
#SyncManager.register('Event', threading.Event, EventProxy)
#SyncManager.register('Lock', threading.Lock, AcquirerProxy)
#SyncManager.register('RLock', threading.RLock, AcquirerProxy)
#SyncManager.register('Semaphore', threading.Semaphore, AcquirerProxy)
#SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore,
#					 AcquirerProxy)
#SyncManager.register('Condition', threading.Condition, ConditionProxy)
#SyncManager.register('Pool', Pool, PoolProxy)
#SyncManager.register('list', list, ListProxy)
#SyncManager.register('dict', dict, DictProxy)
#SyncManager.register('Value', Value, ValueProxy)
#SyncManager.register('Array', Array, ArrayProxy)
#SyncManager.register('Namespace', Namespace, NamespaceProxy)
#
## types returned by methods of PoolProxy
#SyncManager.register('Iterator', proxytype=IteratorProxy, create_method=False)
#SyncManager.register('AsyncResult', create_method=False